commit
44c2df3c12
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -49,6 +49,7 @@ ipch/
|
||||||
# Ignore compiled binaries
|
# Ignore compiled binaries
|
||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
|
*.a
|
||||||
!raylib.rc.o
|
!raylib.rc.o
|
||||||
|
|
||||||
# Ignore all examples files
|
# Ignore all examples files
|
||||||
|
|
|
||||||
57
src/core.c
57
src/core.c
|
|
@ -707,7 +707,7 @@ bool WindowShouldClose(void)
|
||||||
{
|
{
|
||||||
#if defined(PLATFORM_WEB)
|
#if defined(PLATFORM_WEB)
|
||||||
// Emterpreter-Async required to run sync code
|
// Emterpreter-Async required to run sync code
|
||||||
// https://github.com/kripken/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
|
// https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
|
||||||
// By default, this function is never called on a web-ready raylib example because we encapsulate
|
// By default, this function is never called on a web-ready raylib example because we encapsulate
|
||||||
// frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
|
// frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
|
||||||
// but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
|
// but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
|
||||||
|
|
@ -1254,6 +1254,10 @@ void EndTextureMode(void)
|
||||||
|
|
||||||
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
|
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
|
||||||
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
|
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
|
||||||
|
|
||||||
|
// Reset current screen size
|
||||||
|
currentWidth = GetScreenWidth();
|
||||||
|
currentHeight = GetScreenHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a ray trace from mouse position
|
// Returns a ray trace from mouse position
|
||||||
|
|
@ -1426,11 +1430,11 @@ Vector3 ColorToHSV(Color color)
|
||||||
Vector3 hsv = { 0.0f, 0.0f, 0.0f };
|
Vector3 hsv = { 0.0f, 0.0f, 0.0f };
|
||||||
float min, max, delta;
|
float min, max, delta;
|
||||||
|
|
||||||
min = rgb.x < rgb.y ? rgb.x : rgb.y;
|
min = rgb.x < rgb.y? rgb.x : rgb.y;
|
||||||
min = min < rgb.z ? min : rgb.z;
|
min = min < rgb.z? min : rgb.z;
|
||||||
|
|
||||||
max = rgb.x > rgb.y ? rgb.x : rgb.y;
|
max = rgb.x > rgb.y? rgb.x : rgb.y;
|
||||||
max = max > rgb.z ? max : rgb.z;
|
max = max > rgb.z? max : rgb.z;
|
||||||
|
|
||||||
hsv.z = max; // Value
|
hsv.z = max; // Value
|
||||||
delta = max - min;
|
delta = max - min;
|
||||||
|
|
@ -1481,25 +1485,25 @@ Color ColorFromHSV(Vector3 hsv)
|
||||||
// Red channel
|
// Red channel
|
||||||
float k = fmod((5.0f + h/60.0f), 6);
|
float k = fmod((5.0f + h/60.0f), 6);
|
||||||
float t = 4.0f - k;
|
float t = 4.0f - k;
|
||||||
k = (t < k) ? t : k;
|
k = (t < k)? t : k;
|
||||||
k = (k < 1) ? k : 1;
|
k = (k < 1)? k : 1;
|
||||||
k = (k > 0) ? k : 0;
|
k = (k > 0)? k : 0;
|
||||||
color.r = (v - v*s*k)*255;
|
color.r = (v - v*s*k)*255;
|
||||||
|
|
||||||
// Green channel
|
// Green channel
|
||||||
k = fmod((3.0f + h/60.0f), 6);
|
k = fmod((3.0f + h/60.0f), 6);
|
||||||
t = 4.0f - k;
|
t = 4.0f - k;
|
||||||
k = (t < k) ? t : k;
|
k = (t < k)? t : k;
|
||||||
k = (k < 1) ? k : 1;
|
k = (k < 1)? k : 1;
|
||||||
k = (k > 0) ? k : 0;
|
k = (k > 0)? k : 0;
|
||||||
color.g = (v - v*s*k)*255;
|
color.g = (v - v*s*k)*255;
|
||||||
|
|
||||||
// Blue channel
|
// Blue channel
|
||||||
k = fmod((1.0f + h/60.0f), 6);
|
k = fmod((1.0f + h/60.0f), 6);
|
||||||
t = 4.0f - k;
|
t = 4.0f - k;
|
||||||
k = (t < k) ? t : k;
|
k = (t < k)? t : k;
|
||||||
k = (k < 1) ? k : 1;
|
k = (k < 1)? k : 1;
|
||||||
k = (k > 0) ? k : 0;
|
k = (k > 0)? k : 0;
|
||||||
color.b = (v - v*s*k)*255;
|
color.b = (v - v*s*k)*255;
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
|
|
@ -1673,7 +1677,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
|
||||||
|
|
||||||
// NOTE: strrchr() returns a pointer to the last occurrence of character
|
// NOTE: strrchr() returns a pointer to the last occurrence of character
|
||||||
lastDot = strrchr(result, nameDot);
|
lastDot = strrchr(result, nameDot);
|
||||||
lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep);
|
lastSep = (pathSep == 0)? NULL : strrchr(result, pathSep);
|
||||||
|
|
||||||
if (lastDot != NULL) // Check if it has an extension separator...
|
if (lastDot != NULL) // Check if it has an extension separator...
|
||||||
{
|
{
|
||||||
|
|
@ -1913,14 +1917,13 @@ void OpenURL(const char *url)
|
||||||
char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char));
|
char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char));
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
sprintf(cmd, "explorer '%s'", url);
|
sprintf(cmd, "explorer %s", url);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
|
sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
sprintf(cmd, "open '%s'", url);
|
sprintf(cmd, "open '%s'", url);
|
||||||
#endif
|
#endif
|
||||||
system(cmd);
|
system(cmd);
|
||||||
|
|
||||||
free(cmd);
|
free(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3188,7 +3191,7 @@ static void PollInputEvents(void)
|
||||||
|
|
||||||
// Poll Events (registered events)
|
// Poll Events (registered events)
|
||||||
// NOTE: Activity is paused if not enabled (appEnabled)
|
// NOTE: Activity is paused if not enabled (appEnabled)
|
||||||
while ((ident = ALooper_pollAll(appEnabled ? 0 : -1, NULL, &events,(void**)&source)) >= 0)
|
while ((ident = ALooper_pollAll(appEnabled? 0 : -1, NULL, &events,(void**)&source)) >= 0)
|
||||||
{
|
{
|
||||||
// Process this event
|
// Process this event
|
||||||
if (source != NULL) source->process(androidApp, source);
|
if (source != NULL) source->process(androidApp, source);
|
||||||
|
|
@ -3274,9 +3277,9 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
|
||||||
char path[512] = { 0 };
|
char path[512] = { 0 };
|
||||||
#if defined(PLATFORM_ANDROID)
|
#if defined(PLATFORM_ANDROID)
|
||||||
strcpy(path, internalDataPath);
|
strcpy(path, internalDataPath);
|
||||||
strcat(path, TextFormat("/screenrec%03i.gif", screenshotCounter));
|
strcat(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
|
||||||
#else
|
#else
|
||||||
strcpy(path, TextFormat("/screenrec%03i.gif", screenshotCounter));
|
strcpy(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: delay represents the time between frames in the gif, if we capture a gif frame every
|
// NOTE: delay represents the time between frames in the gif, if we capture a gif frame every
|
||||||
|
|
@ -3768,7 +3771,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches,
|
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" : "");
|
event->ctrlKey? " CTRL" : "", event->shiftKey? " SHIFT" : "", event->altKey? " ALT" : "", event->metaKey? " META" : "");
|
||||||
|
|
||||||
for (int i = 0; i < event->numTouches; ++i)
|
for (int i = 0; i < event->numTouches; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -3822,7 +3825,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n",
|
printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n",
|
||||||
eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state",
|
eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
|
||||||
gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
|
gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
|
||||||
|
|
||||||
for(int i = 0; i < gamepadEvent->numAxes; ++i) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]);
|
for(int i = 0; i < gamepadEvent->numAxes; ++i) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]);
|
||||||
|
|
@ -4186,11 +4189,11 @@ static void EventThreadSpawn(char *device)
|
||||||
{
|
{
|
||||||
// Looks like a interesting device
|
// Looks like a interesting device
|
||||||
TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device,
|
TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device,
|
||||||
worker->isMouse ? "mouse " : "",
|
worker->isMouse? "mouse " : "",
|
||||||
worker->isMultitouch ? "multitouch " : "",
|
worker->isMultitouch? "multitouch " : "",
|
||||||
worker->isTouch ? "touchscreen " : "",
|
worker->isTouch? "touchscreen " : "",
|
||||||
worker->isGamepad ? "gamepad " : "",
|
worker->isGamepad? "gamepad " : "",
|
||||||
worker->isKeyboard ? "keyboard " : "");
|
worker->isKeyboard? "keyboard " : "");
|
||||||
|
|
||||||
// Create a thread for this device
|
// Create a thread for this device
|
||||||
int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
|
int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
|
||||||
|
|
|
||||||
51
src/external/cgltf.h
vendored
51
src/external/cgltf.h
vendored
|
|
@ -1,6 +1,49 @@
|
||||||
/**
|
/**
|
||||||
* cgltf - a single-file glTF 2.0 parser written in C99.
|
* cgltf - a single-file glTF 2.0 parser written in C99.
|
||||||
|
*
|
||||||
|
* Version: 1.0
|
||||||
|
*
|
||||||
|
* Website: https://github.com/jkuhlmann/cgltf
|
||||||
|
*
|
||||||
* Distributed under the MIT License, see notice at the end of this file.
|
* Distributed under the MIT License, see notice at the end of this file.
|
||||||
|
*
|
||||||
|
* Building:
|
||||||
|
* Include this file where you need the struct and function
|
||||||
|
* declarations. Have exactly one source file where you define
|
||||||
|
* `CGLTF_IMPLEMENTATION` before including this file to get the
|
||||||
|
* function definitions.
|
||||||
|
*
|
||||||
|
* Reference:
|
||||||
|
* `cgltf_result cgltf_parse(const cgltf_options*, const void*,
|
||||||
|
* cgltf_size, cgltf_data**)` parses both glTF and GLB data. If
|
||||||
|
* this function returns `cgltf_result_success`, you have to call
|
||||||
|
* `cgltf_free()` on the created `cgltf_data*` variable.
|
||||||
|
* Note that contents of external files for buffers and images are not
|
||||||
|
* automatically loaded. You'll need to read these files yourself using
|
||||||
|
* URIs in the `cgltf_data` structure.
|
||||||
|
*
|
||||||
|
* `cgltf_options` is the struct passed to `cgltf_parse()` to control
|
||||||
|
* parts of the parsing process. You can use it to force the file type
|
||||||
|
* and provide memory allocation callbacks. Should be zero-initialized
|
||||||
|
* to trigger default behavior.
|
||||||
|
*
|
||||||
|
* `cgltf_data` is the struct allocated and filled by `cgltf_parse()`.
|
||||||
|
* It generally mirrors the glTF format as described by the spec (see
|
||||||
|
* https://github.com/KhronosGroup/glTF/tree/master/specification/2.0).
|
||||||
|
*
|
||||||
|
* `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data`
|
||||||
|
* variable.
|
||||||
|
*
|
||||||
|
* `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*,
|
||||||
|
* const char*)` can be optionally called to open and read buffer
|
||||||
|
* files using the `FILE*` APIs.
|
||||||
|
*
|
||||||
|
* `cgltf_result cgltf_parse_file(const cgltf_options* options, const
|
||||||
|
* char* path, cgltf_data** out_data)` can be used to open the given
|
||||||
|
* file using `FILE*` APIs and parse the data using `cgltf_parse()`.
|
||||||
|
*
|
||||||
|
* `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional
|
||||||
|
* checks to make sure the parsed glTF data is valid.
|
||||||
*/
|
*/
|
||||||
#ifndef CGLTF_H_INCLUDED__
|
#ifndef CGLTF_H_INCLUDED__
|
||||||
#define CGLTF_H_INCLUDED__
|
#define CGLTF_H_INCLUDED__
|
||||||
|
|
@ -462,6 +505,10 @@ void cgltf_free(cgltf_data* data);
|
||||||
void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
|
void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
|
||||||
void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
|
void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* #ifndef CGLTF_H_INCLUDED__ */
|
#endif /* #ifndef CGLTF_H_INCLUDED__ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -4266,10 +4313,6 @@ static void jsmn_init(jsmn_parser *parser) {
|
||||||
|
|
||||||
#endif /* #ifdef CGLTF_IMPLEMENTATION */
|
#endif /* #ifdef CGLTF_IMPLEMENTATION */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* cgltf is distributed under MIT license:
|
/* cgltf is distributed under MIT license:
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018 Johannes Kuhlmann
|
* Copyright (c) 2018 Johannes Kuhlmann
|
||||||
|
|
|
||||||
BIN
src/libraylib.a
BIN
src/libraylib.a
Binary file not shown.
32
src/models.c
32
src/models.c
|
|
@ -655,12 +655,16 @@ Mesh LoadMesh(const char *fileName)
|
||||||
TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName);
|
TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(SUPPORT_MESH_GENERATION)
|
||||||
if (mesh.vertexCount == 0)
|
if (mesh.vertexCount == 0)
|
||||||
{
|
{
|
||||||
TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!");
|
TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!");
|
||||||
mesh = GenMeshCube(1.0f, 1.0f, 1.0f);
|
mesh = GenMeshCube(1.0f, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh)
|
else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh)
|
||||||
|
#else
|
||||||
|
rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh)
|
||||||
|
#endif
|
||||||
|
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
@ -2256,7 +2260,7 @@ void MeshTangents(Mesh *mesh)
|
||||||
float t2 = uv3.y - uv1.y;
|
float t2 = uv3.y - uv1.y;
|
||||||
|
|
||||||
float div = s1*t2 - s2*t1;
|
float div = s1*t2 - s2*t1;
|
||||||
float r = (div == 0.0f) ? 0.0f : 1.0f/div;
|
float r = (div == 0.0f)? 0.0f : 1.0f/div;
|
||||||
|
|
||||||
Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
|
Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
|
||||||
Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
|
Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
|
||||||
|
|
@ -2289,7 +2293,7 @@ void MeshTangents(Mesh *mesh)
|
||||||
mesh->tangents[i*4 + 0] = tangent.x;
|
mesh->tangents[i*4 + 0] = tangent.x;
|
||||||
mesh->tangents[i*4 + 1] = tangent.y;
|
mesh->tangents[i*4 + 1] = tangent.y;
|
||||||
mesh->tangents[i*4 + 2] = tangent.z;
|
mesh->tangents[i*4 + 2] = tangent.z;
|
||||||
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f) ? -1.0f : 1.0f;
|
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2308,7 +2312,7 @@ void MeshBinormals(Mesh *mesh)
|
||||||
Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
|
Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
|
||||||
float tangentW = mesh->tangents[i*4 + 3];
|
float tangentW = mesh->tangents[i*4 + 3];
|
||||||
|
|
||||||
// TODO: Register computed binormal in mesh->binormal ?
|
// TODO: Register computed binormal in mesh->binormal?
|
||||||
// Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW);
|
// Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2635,7 +2639,7 @@ static Material LoadMTL(const char *fileName)
|
||||||
} break;
|
} break;
|
||||||
case 'e': // Ke float float float Emmisive color (RGB)
|
case 'e': // Ke float float float Emmisive color (RGB)
|
||||||
{
|
{
|
||||||
// TODO: Support Ke ?
|
// TODO: Support Ke?
|
||||||
} break;
|
} break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
@ -2744,17 +2748,17 @@ static Mesh LoadIQM(const char *fileName)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_GLTF)
|
#if defined(SUPPORT_FILEFORMAT_GLTF)
|
||||||
// Load GLTF mesh data
|
// Load glTF mesh data
|
||||||
static Mesh LoadGLTF(const char *fileName)
|
static Mesh LoadGLTF(const char *fileName)
|
||||||
{
|
{
|
||||||
Mesh mesh = { 0 };
|
Mesh mesh = { 0 };
|
||||||
|
|
||||||
// GLTF file loading
|
// glTF file loading
|
||||||
FILE *gltfFile = fopen(fileName, "rb");
|
FILE *gltfFile = fopen(fileName, "rb");
|
||||||
|
|
||||||
if (gltfFile == NULL)
|
if (gltfFile == NULL)
|
||||||
{
|
{
|
||||||
TraceLog(LOG_WARNING, "[%s] GLTF file could not be opened", fileName);
|
TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName);
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2767,21 +2771,27 @@ static Mesh LoadGLTF(const char *fileName)
|
||||||
|
|
||||||
fclose(gltfFile);
|
fclose(gltfFile);
|
||||||
|
|
||||||
// GLTF data loading
|
// glTF data loading
|
||||||
cgltf_options options = {0};
|
cgltf_options options = {0};
|
||||||
cgltf_data data;
|
cgltf_data data;
|
||||||
cgltf_result result = cgltf_parse(&options, buffer, size, &data);
|
cgltf_result result = cgltf_parse(&options, buffer, size, &data);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
if (result == cgltf_result_success)
|
if (result == cgltf_result_success)
|
||||||
{
|
{
|
||||||
printf("Type: %u\n", data.file_type);
|
printf("Type: %u\n", data.file_type);
|
||||||
printf("Version: %d\n", data.version);
|
printf("Version: %d\n", data.version);
|
||||||
printf("Meshes: %lu\n", data.meshes_count);
|
printf("Meshes: %lu\n", data.meshes_count);
|
||||||
}
|
|
||||||
else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName);
|
|
||||||
|
|
||||||
free(buffer);
|
// TODO: Process glTF data and map to mesh
|
||||||
|
|
||||||
|
// NOTE: data.buffers[] and data.images[] should be loaded
|
||||||
|
// using buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName);
|
||||||
|
|
||||||
cgltf_free(&data);
|
cgltf_free(&data);
|
||||||
|
}
|
||||||
|
else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName);
|
||||||
|
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
src/raudio.c
52
src/raudio.c
|
|
@ -712,11 +712,13 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioBuffer->pitch = pitch;
|
float pitchMul = pitch / audioBuffer->pitch;
|
||||||
|
|
||||||
// Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches
|
// Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches
|
||||||
// will make the sound faster; lower pitches make it slower.
|
// will make the sound faster; lower pitches make it slower.
|
||||||
mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.src.config.sampleRateOut / (float)audioBuffer->dsp.src.config.sampleRateIn) / pitch) * audioBuffer->dsp.src.config.sampleRateIn);
|
mal_uint32 newOutputSampleRate = (mal_uint32)((float)audioBuffer->dsp.src.config.sampleRateOut / pitchMul);
|
||||||
|
audioBuffer->pitch *= (float)audioBuffer->dsp.src.config.sampleRateOut / newOutputSampleRate;
|
||||||
|
|
||||||
mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate);
|
mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -767,7 +769,11 @@ Wave LoadWave(const char *fileName)
|
||||||
{
|
{
|
||||||
Wave wave = { 0 };
|
Wave wave = { 0 };
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||||
if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
|
if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
|
else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -830,7 +836,7 @@ Sound LoadSoundFromWave(Wave wave)
|
||||||
//
|
//
|
||||||
// I have decided on the first option because it offloads work required for the format conversion to the to the loading stage.
|
// I have decided on the first option because it offloads work required for the format conversion to the to the loading stage.
|
||||||
// The downside to this is that it uses more memory if the original sound is u8 or s16.
|
// The downside to this is that it uses more memory if the original sound is u8 or s16.
|
||||||
mal_format formatIn = ((wave.sampleSize == 8) ? mal_format_u8 : ((wave.sampleSize == 16) ? mal_format_s16 : mal_format_f32));
|
mal_format formatIn = ((wave.sampleSize == 8)? mal_format_u8 : ((wave.sampleSize == 16)? mal_format_s16 : mal_format_f32));
|
||||||
mal_uint32 frameCountIn = wave.sampleCount/wave.channels;
|
mal_uint32 frameCountIn = wave.sampleCount/wave.channels;
|
||||||
|
|
||||||
mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn);
|
mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn);
|
||||||
|
|
@ -887,7 +893,11 @@ void ExportWave(Wave wave, const char *fileName)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||||
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
else if (IsFileExtension(fileName, ".raw"))
|
else if (IsFileExtension(fileName, ".raw"))
|
||||||
{
|
{
|
||||||
// Export raw sample data (without header)
|
// Export raw sample data (without header)
|
||||||
|
|
@ -938,7 +948,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
|
||||||
|
|
||||||
// Write byte data as hexadecimal text
|
// Write byte data as hexadecimal text
|
||||||
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
|
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
|
||||||
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]);
|
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]);
|
||||||
fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]);
|
fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]);
|
||||||
|
|
||||||
fclose(txtFile);
|
fclose(txtFile);
|
||||||
|
|
@ -989,8 +999,8 @@ void SetSoundPitch(Sound sound, float pitch)
|
||||||
// Convert wave data to desired format
|
// Convert wave data to desired format
|
||||||
void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
||||||
{
|
{
|
||||||
mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32));
|
mal_format formatIn = ((wave->sampleSize == 8)? mal_format_u8 : ((wave->sampleSize == 16)? mal_format_s16 : mal_format_f32));
|
||||||
mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32));
|
mal_format formatOut = (( sampleSize == 8)? mal_format_u8 : (( sampleSize == 16)? mal_format_s16 : mal_format_f32));
|
||||||
|
|
||||||
mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
|
mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
|
||||||
|
|
||||||
|
|
@ -1087,6 +1097,7 @@ Music LoadMusicStream(const char *fileName)
|
||||||
Music music = (MusicData *)malloc(sizeof(MusicData));
|
Music music = (MusicData *)malloc(sizeof(MusicData));
|
||||||
bool musicLoaded = true;
|
bool musicLoaded = true;
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
if (IsFileExtension(fileName, ".ogg"))
|
if (IsFileExtension(fileName, ".ogg"))
|
||||||
{
|
{
|
||||||
// Open ogg audio stream
|
// Open ogg audio stream
|
||||||
|
|
@ -1110,6 +1121,9 @@ Music LoadMusicStream(const char *fileName)
|
||||||
TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
|
TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
else if (IsFileExtension(fileName, ".flac"))
|
else if (IsFileExtension(fileName, ".flac"))
|
||||||
{
|
{
|
||||||
|
|
@ -1202,7 +1216,11 @@ Music LoadMusicStream(const char *fileName)
|
||||||
|
|
||||||
if (!musicLoaded)
|
if (!musicLoaded)
|
||||||
{
|
{
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
|
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
|
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1232,7 +1250,11 @@ void UnloadMusicStream(Music music)
|
||||||
|
|
||||||
CloseAudioStream(music->stream);
|
CloseAudioStream(music->stream);
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
|
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
|
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1297,7 +1319,9 @@ void StopMusicStream(Music music)
|
||||||
// Restart music context
|
// Restart music context
|
||||||
switch (music->ctxType)
|
switch (music->ctxType)
|
||||||
{
|
{
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
|
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
|
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1339,12 +1363,14 @@ void UpdateMusicStream(Music music)
|
||||||
// TODO: Really don't like ctxType thingy...
|
// TODO: Really don't like ctxType thingy...
|
||||||
switch (music->ctxType)
|
switch (music->ctxType)
|
||||||
{
|
{
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
case MUSIC_AUDIO_OGG:
|
case MUSIC_AUDIO_OGG:
|
||||||
{
|
{
|
||||||
// NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
|
// NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
|
||||||
stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount);
|
stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
case MUSIC_AUDIO_FLAC:
|
case MUSIC_AUDIO_FLAC:
|
||||||
{
|
{
|
||||||
|
|
@ -1487,7 +1513,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
|
||||||
stream.channels = 1; // Fallback to mono channel
|
stream.channels = 1; // Fallback to mono channel
|
||||||
}
|
}
|
||||||
|
|
||||||
mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32));
|
mal_format formatIn = ((stream.sampleSize == 8)? mal_format_u8 : ((stream.sampleSize == 16)? mal_format_s16 : mal_format_f32));
|
||||||
|
|
||||||
// The size of a streaming buffer must be at least double the size of a period.
|
// The size of a streaming buffer must be at least double the size of a period.
|
||||||
unsigned int periodSize = device.bufferSizeInFrames/device.periods;
|
unsigned int periodSize = device.bufferSizeInFrames/device.periods;
|
||||||
|
|
@ -1504,7 +1530,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
|
||||||
audioBuffer->looping = true; // Always loop for streaming buffers.
|
audioBuffer->looping = true; // Always loop for streaming buffers.
|
||||||
stream.audioBuffer = audioBuffer;
|
stream.audioBuffer = audioBuffer;
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo");
|
TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo");
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
@ -1542,7 +1568,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Just update whichever sub-buffer is processed.
|
// Just update whichever sub-buffer is processed.
|
||||||
subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0]) ? 0 : 1;
|
subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
|
mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
|
||||||
|
|
@ -1745,7 +1771,7 @@ static Wave LoadWAV(const char *fileName)
|
||||||
// NOTE: subChunkSize comes in bytes, we need to translate it to number of samples
|
// NOTE: subChunkSize comes in bytes, we need to translate it to number of samples
|
||||||
wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels;
|
wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels;
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "[%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
|
TraceLog(LOG_INFO, "[%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1866,7 +1892,7 @@ static Wave LoadOGG(const char *fileName)
|
||||||
|
|
||||||
TraceLog(LOG_DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg);
|
TraceLog(LOG_DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg);
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
|
TraceLog(LOG_INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
|
||||||
|
|
||||||
stb_vorbis_close(oggFile);
|
stb_vorbis_close(oggFile);
|
||||||
}
|
}
|
||||||
|
|
@ -1893,7 +1919,7 @@ static Wave LoadFLAC(const char *fileName)
|
||||||
if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] FLAC channels number (%i) not supported", fileName, wave.channels);
|
if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] FLAC channels number (%i) not supported", fileName, wave.channels);
|
||||||
|
|
||||||
if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] FLAC data could not be loaded", fileName);
|
if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] FLAC data could not be loaded", fileName);
|
||||||
else TraceLog(LOG_INFO, "[%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
|
else TraceLog(LOG_INFO, "[%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
|
||||||
|
|
||||||
return wave;
|
return wave;
|
||||||
}
|
}
|
||||||
|
|
@ -1920,7 +1946,7 @@ static Wave LoadMP3(const char *fileName)
|
||||||
if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels);
|
if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels);
|
||||||
|
|
||||||
if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] MP3 data could not be loaded", fileName);
|
if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] MP3 data could not be loaded", fileName);
|
||||||
else TraceLog(LOG_INFO, "[%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
|
else TraceLog(LOG_INFO, "[%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
|
||||||
|
|
||||||
return wave;
|
return wave;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/rlgl.h
10
src/rlgl.h
|
|
@ -775,8 +775,8 @@ typedef struct DrawCall {
|
||||||
#endif
|
#endif
|
||||||
"void main() \n"
|
"void main() \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec2 lensCenter = fragTexCoord.x < 0.5 ? leftLensCenter : rightLensCenter; \n"
|
" vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; \n"
|
||||||
" vec2 screenCenter = fragTexCoord.x < 0.5 ? leftScreenCenter : rightScreenCenter; \n"
|
" vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; \n"
|
||||||
" vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n"
|
" vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n"
|
||||||
" float rSq = theta.x*theta.x + theta.y*theta.y; \n"
|
" float rSq = theta.x*theta.x + theta.y*theta.y; \n"
|
||||||
" vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n"
|
" vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n"
|
||||||
|
|
@ -1136,7 +1136,7 @@ void rlEnd(void)
|
||||||
// TODO: System could be improved (a bit) just storing every draw alignment value
|
// TODO: System could be improved (a bit) just storing every draw alignment value
|
||||||
// and adding it to vertexOffset on drawing... maybe in a future...
|
// and adding it to vertexOffset on drawing... maybe in a future...
|
||||||
int vertexCount = draws[drawsCounter - 1].vertexCount;
|
int vertexCount = draws[drawsCounter - 1].vertexCount;
|
||||||
int vertexToAlign = (vertexCount >= 4) ? vertexCount%4 : (4 - vertexCount%4);
|
int vertexToAlign = (vertexCount >= 4)? vertexCount%4 : (4 - vertexCount%4);
|
||||||
for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1);
|
for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1);
|
||||||
|
|
||||||
// Make sure vertexCount is the same for vertices, texcoords, colors and normals
|
// Make sure vertexCount is the same for vertices, texcoords, colors and normals
|
||||||
|
|
@ -1233,7 +1233,7 @@ void rlTexCoord2f(float x, float y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define one vertex (normal)
|
// Define one vertex (normal)
|
||||||
// NOTE: Normals limited to TRIANGLES only ?
|
// NOTE: Normals limited to TRIANGLES only?
|
||||||
void rlNormal3f(float x, float y, float z)
|
void rlNormal3f(float x, float y, float z)
|
||||||
{
|
{
|
||||||
// TODO: Normals usage...
|
// TODO: Normals usage...
|
||||||
|
|
@ -2206,7 +2206,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth
|
||||||
target.depth.id = rlLoadTextureDepth(width, height, depthBits, !useDepthTexture);
|
target.depth.id = rlLoadTextureDepth(width, height, depthBits, !useDepthTexture);
|
||||||
target.depth.width = width;
|
target.depth.width = width;
|
||||||
target.depth.height = height;
|
target.depth.height = height;
|
||||||
target.depth.format = 19; //DEPTH_COMPONENT_24BIT ?
|
target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
|
||||||
target.depth.mipmaps = 1;
|
target.depth.mipmaps = 1;
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
|
||||||
rlPushMatrix();
|
rlPushMatrix();
|
||||||
rlTranslatef((float)startPos.x, (float)startPos.y, 0.0f);
|
rlTranslatef((float)startPos.x, (float)startPos.y, 0.0f);
|
||||||
rlRotatef(RAD2DEG*angle, 0.0f, 0.0f, 1.0f);
|
rlRotatef(RAD2DEG*angle, 0.0f, 0.0f, 1.0f);
|
||||||
rlTranslatef(0, (thick > 1.0f) ? -thick/2.0f : -1.0f, 0.0f);
|
rlTranslatef(0, (thick > 1.0f)? -thick/2.0f : -1.0f, 0.0f);
|
||||||
|
|
||||||
rlBegin(RL_QUADS);
|
rlBegin(RL_QUADS);
|
||||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||||
|
|
@ -742,7 +742,7 @@ static Texture2D GetShapesTexture(void)
|
||||||
recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
|
recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
|
||||||
#else
|
#else
|
||||||
texShapes = GetTextureDefault(); // Use default white texture
|
texShapes = GetTextureDefault(); // Use default white texture
|
||||||
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f };
|
recTexShapes = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f };
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
30
src/text.c
30
src/text.c
|
|
@ -306,9 +306,10 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
|
||||||
Font font = { 0 };
|
Font font = { 0 };
|
||||||
|
|
||||||
font.baseSize = fontSize;
|
font.baseSize = fontSize;
|
||||||
font.charsCount = (charsCount > 0) ? charsCount : 95;
|
font.charsCount = (charsCount > 0)? charsCount : 95;
|
||||||
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
|
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_TTF)
|
||||||
if (font.chars != NULL)
|
if (font.chars != NULL)
|
||||||
{
|
{
|
||||||
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
|
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
|
||||||
|
|
@ -316,6 +317,9 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
|
||||||
UnloadImage(atlas);
|
UnloadImage(atlas);
|
||||||
}
|
}
|
||||||
else font = GetFontDefault();
|
else font = GetFontDefault();
|
||||||
|
#else
|
||||||
|
font = GetFontDefault();
|
||||||
|
#endif
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +430,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
|
||||||
spriteFont.chars[i].offsetX = 0;
|
spriteFont.chars[i].offsetX = 0;
|
||||||
spriteFont.chars[i].offsetY = 0;
|
spriteFont.chars[i].offsetY = 0;
|
||||||
spriteFont.chars[i].advanceX = 0;
|
spriteFont.chars[i].advanceX = 0;
|
||||||
|
spriteFont.chars[i].data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
spriteFont.baseSize = (int)spriteFont.chars[0].rec.height;
|
spriteFont.baseSize = (int)spriteFont.chars[0].rec.height;
|
||||||
|
|
@ -449,6 +454,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||||
|
|
||||||
CharInfo *chars = NULL;
|
CharInfo *chars = NULL;
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_TTF)
|
||||||
// Load font data (including pixel data) from TTF file
|
// Load font data (including pixel data) from TTF file
|
||||||
// NOTE: Loaded information should be enough to generate font image atlas,
|
// NOTE: Loaded information should be enough to generate font image atlas,
|
||||||
// using any packaging method
|
// using any packaging method
|
||||||
|
|
@ -478,7 +484,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||||
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
|
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
|
||||||
|
|
||||||
// In case no chars count provided, default to 95
|
// In case no chars count provided, default to 95
|
||||||
charsCount = (charsCount > 0) ? charsCount : 95;
|
charsCount = (charsCount > 0)? charsCount : 95;
|
||||||
|
|
||||||
// Fill fontChars in case not provided externally
|
// Fill fontChars in case not provided externally
|
||||||
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
|
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
|
||||||
|
|
@ -506,6 +512,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||||
|
|
||||||
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
||||||
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
||||||
|
else chars[i].data = NULL;
|
||||||
|
|
||||||
if (type == FONT_BITMAP)
|
if (type == FONT_BITMAP)
|
||||||
{
|
{
|
||||||
|
|
@ -537,18 +544,22 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||||
if (genFontChars) free(fontChars);
|
if (genFontChars) free(fontChars);
|
||||||
}
|
}
|
||||||
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
|
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
|
||||||
|
#else
|
||||||
|
TraceLog(LOG_WARNING, "[%s] TTF support is disabled", fileName);
|
||||||
|
#endif
|
||||||
|
|
||||||
return chars;
|
return chars;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate image font atlas using chars info
|
// Generate image font atlas using chars info
|
||||||
// NOTE: Packing method: 0-Default, 1-Skyline
|
// NOTE: Packing method: 0-Default, 1-Skyline
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_TTF)
|
||||||
Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod)
|
Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod)
|
||||||
{
|
{
|
||||||
Image atlas = { 0 };
|
Image atlas = { 0 };
|
||||||
|
|
||||||
// In case no chars count provided we suppose default of 95
|
// In case no chars count provided we suppose default of 95
|
||||||
charsCount = (charsCount > 0) ? charsCount : 95;
|
charsCount = (charsCount > 0)? charsCount : 95;
|
||||||
|
|
||||||
// Calculate image size based on required pixel area
|
// Calculate image size based on required pixel area
|
||||||
// NOTE 1: Image is forced to be squared and POT... very conservative!
|
// NOTE 1: Image is forced to be squared and POT... very conservative!
|
||||||
|
|
@ -667,6 +678,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||||
|
|
||||||
return atlas;
|
return atlas;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Unload Font from GPU memory (VRAM)
|
// Unload Font from GPU memory (VRAM)
|
||||||
void UnloadFont(Font font)
|
void UnloadFont(Font font)
|
||||||
|
|
@ -674,6 +686,11 @@ void UnloadFont(Font font)
|
||||||
// NOTE: Make sure spriteFont is not default font (fallback)
|
// NOTE: Make sure spriteFont is not default font (fallback)
|
||||||
if (font.texture.id != GetFontDefault().texture.id)
|
if (font.texture.id != GetFontDefault().texture.id)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < font.charsCount; i++)
|
||||||
|
{
|
||||||
|
if(font.chars[i].data != NULL)
|
||||||
|
free(font.chars[i].data);
|
||||||
|
}
|
||||||
UnloadTexture(font.texture);
|
UnloadTexture(font.texture);
|
||||||
free(font.chars);
|
free(font.chars);
|
||||||
|
|
||||||
|
|
@ -846,7 +863,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
|
|
||||||
if ((textOffsetX + glyphWidth + 1) >= rec.width)
|
if ((textOffsetX + glyphWidth + 1) >= rec.width)
|
||||||
{
|
{
|
||||||
endLine = (endLine < 1) ? i : endLine;
|
endLine = (endLine < 1)? i : endLine;
|
||||||
if (i == endLine) endLine -= 1;
|
if (i == endLine) endLine -= 1;
|
||||||
if ((startLine + 1) == endLine) endLine = i - 1;
|
if ((startLine + 1) == endLine) endLine = i - 1;
|
||||||
state = !state;
|
state = !state;
|
||||||
|
|
@ -887,7 +904,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
textOffsetX = 0;
|
textOffsetX = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((textOffsetY + (int)((font.baseSize + font.baseSize/2)*scaleFactor)) > rec.height) break;
|
if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
|
||||||
|
|
||||||
//draw selected
|
//draw selected
|
||||||
bool isGlyphSelected = false;
|
bool isGlyphSelected = false;
|
||||||
|
|
@ -906,7 +923,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
||||||
font.chars[index].rec.width*scaleFactor,
|
font.chars[index].rec.width*scaleFactor,
|
||||||
font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f,
|
font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f,
|
||||||
(!isGlyphSelected) ? tint : selectText);
|
(!isGlyphSelected)? tint : selectText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1428,6 +1445,7 @@ static Font LoadBMFont(const char *fileName)
|
||||||
font.chars[i].offsetX = charOffsetX;
|
font.chars[i].offsetX = charOffsetX;
|
||||||
font.chars[i].offsetY = charOffsetY;
|
font.chars[i].offsetY = charOffsetY;
|
||||||
font.chars[i].advanceX = charAdvanceX;
|
font.chars[i].advanceX = charAdvanceX;
|
||||||
|
font.chars[i].data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fntFile);
|
fclose(fntFile);
|
||||||
|
|
|
||||||
274
src/textures.c
274
src/textures.c
|
|
@ -182,7 +182,11 @@ Image LoadImage(const char *fileName)
|
||||||
{
|
{
|
||||||
Image image = { 0 };
|
Image image = { 0 };
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_PNG)
|
||||||
if ((IsFileExtension(fileName, ".png"))
|
if ((IsFileExtension(fileName, ".png"))
|
||||||
|
#else
|
||||||
|
if ((false)
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_BMP)
|
#if defined(SUPPORT_FILEFORMAT_BMP)
|
||||||
|| (IsFileExtension(fileName, ".bmp"))
|
|| (IsFileExtension(fileName, ".bmp"))
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -398,90 +402,6 @@ Texture2D LoadTextureFromImage(Image image)
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load cubemap from image, multiple image cubemap layouts supported
|
|
||||||
TextureCubemap LoadTextureCubemap(Image image, int layoutType)
|
|
||||||
{
|
|
||||||
TextureCubemap cubemap = { 0 };
|
|
||||||
|
|
||||||
if (layoutType == CUBEMAP_AUTO_DETECT) // Try to automatically guess layout type
|
|
||||||
{
|
|
||||||
// Check image width/height to determine the type of cubemap provided
|
|
||||||
if (image.width > image.height)
|
|
||||||
{
|
|
||||||
if ((image.width/6) == image.height) { layoutType = CUBEMAP_LINE_HORIZONTAL; cubemap.width = image.width/6; }
|
|
||||||
else if ((image.width/4) == (image.height/3)) { layoutType = CUBEMAP_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
|
|
||||||
else if (image.width >= (int)((float)image.height*1.85f)) { layoutType = CUBEMAP_PANORAMA; cubemap.width = image.width/4; }
|
|
||||||
}
|
|
||||||
else if (image.height > image.width)
|
|
||||||
{
|
|
||||||
if ((image.height/6) == image.width) { layoutType = CUBEMAP_LINE_VERTICAL; cubemap.width = image.height/6; }
|
|
||||||
else if ((image.width/3) == (image.height/4)) { layoutType = CUBEMAP_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
|
|
||||||
}
|
|
||||||
|
|
||||||
cubemap.height = cubemap.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = cubemap.width;
|
|
||||||
|
|
||||||
if (layoutType != CUBEMAP_AUTO_DETECT)
|
|
||||||
{
|
|
||||||
//unsigned int dataSize = GetPixelDataSize(size, size, format);
|
|
||||||
//void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column
|
|
||||||
|
|
||||||
Image faces = { 0 }; // Vertical column image
|
|
||||||
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
|
|
||||||
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, size, size };
|
|
||||||
|
|
||||||
if (layoutType == CUBEMAP_LINE_VERTICAL)
|
|
||||||
{
|
|
||||||
faces = image;
|
|
||||||
for (int i = 0; i < 6; i++) faceRecs[i].y = size*i;
|
|
||||||
}
|
|
||||||
else if (layoutType == CUBEMAP_PANORAMA)
|
|
||||||
{
|
|
||||||
// TODO: Convert panorama image to square faces...
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (layoutType == CUBEMAP_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = size*i;
|
|
||||||
else if (layoutType == CUBEMAP_CROSS_THREE_BY_FOUR)
|
|
||||||
{
|
|
||||||
faceRecs[0].x = size; faceRecs[0].y = size;
|
|
||||||
faceRecs[1].x = size; faceRecs[1].y = 3*size;
|
|
||||||
faceRecs[2].x = size; faceRecs[2].y = 0;
|
|
||||||
faceRecs[3].x = size; faceRecs[3].y = 2*size;
|
|
||||||
faceRecs[4].x = 0; faceRecs[4].y = size;
|
|
||||||
faceRecs[5].x = 2*size; faceRecs[5].y = size;
|
|
||||||
}
|
|
||||||
else if (layoutType == CUBEMAP_CROSS_FOUR_BY_THREE)
|
|
||||||
{
|
|
||||||
faceRecs[0].x = 2*size; faceRecs[0].y = size;
|
|
||||||
faceRecs[1].x = 0; faceRecs[1].y = size;
|
|
||||||
faceRecs[2].x = size; faceRecs[2].y = 0;
|
|
||||||
faceRecs[3].x = size; faceRecs[3].y = 2*size;
|
|
||||||
faceRecs[4].x = size; faceRecs[4].y = size;
|
|
||||||
faceRecs[5].x = 3*size; faceRecs[5].y = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
|
|
||||||
faces = GenImageColor(size, size*6, MAGENTA);
|
|
||||||
ImageFormat(&faces, image.format);
|
|
||||||
|
|
||||||
// TODO: Image formating does not work with compressed textures!
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size });
|
|
||||||
|
|
||||||
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
|
|
||||||
if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded.");
|
|
||||||
|
|
||||||
UnloadImage(faces);
|
|
||||||
}
|
|
||||||
else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected.");
|
|
||||||
|
|
||||||
return cubemap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load texture for rendering (framebuffer)
|
// Load texture for rendering (framebuffer)
|
||||||
// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
|
// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
|
||||||
RenderTexture2D LoadRenderTexture(int width, int height)
|
RenderTexture2D LoadRenderTexture(int width, int height)
|
||||||
|
|
@ -668,7 +588,7 @@ Vector4 *GetImageDataNormalized(Image image)
|
||||||
pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
|
pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
|
||||||
pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
|
pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
|
||||||
pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
|
pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
|
||||||
pixels[i].w = ((pixel & 0b0000000000000001) == 0) ? 0.0f : 1.0f;
|
pixels[i].w = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case UNCOMPRESSED_R5G6B5:
|
case UNCOMPRESSED_R5G6B5:
|
||||||
|
|
@ -825,14 +745,27 @@ void ExportImage(Image image, const char *fileName)
|
||||||
{
|
{
|
||||||
int success = 0;
|
int success = 0;
|
||||||
|
|
||||||
|
#if defined(SUPPORT_IMAGE_EXPORT)
|
||||||
// NOTE: Getting Color array as RGBA unsigned char values
|
// NOTE: Getting Color array as RGBA unsigned char values
|
||||||
unsigned char *imgData = (unsigned char *)GetImageData(image);
|
unsigned char *imgData = (unsigned char *)GetImageData(image);
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_PNG)
|
||||||
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
|
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
|
||||||
|
#else
|
||||||
|
if (false) {}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_BMP)
|
||||||
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
|
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_TGA)
|
||||||
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
|
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_JPG)
|
||||||
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
|
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_KTX)
|
||||||
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
|
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
|
||||||
|
#endif
|
||||||
else if (IsFileExtension(fileName, ".raw"))
|
else if (IsFileExtension(fileName, ".raw"))
|
||||||
{
|
{
|
||||||
// Export raw pixel data (without header)
|
// Export raw pixel data (without header)
|
||||||
|
|
@ -842,10 +775,11 @@ void ExportImage(Image image, const char *fileName)
|
||||||
fclose(rawFile);
|
fclose(rawFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(imgData);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
|
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
|
||||||
else TraceLog(LOG_WARNING, "Image could not be exported.");
|
else TraceLog(LOG_WARNING, "Image could not be exported.");
|
||||||
|
|
||||||
free(imgData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export image as code file (.h) defining an array of bytes
|
// Export image as code file (.h) defining an array of bytes
|
||||||
|
|
@ -880,7 +814,7 @@ void ExportImageAsCode(Image image, const char *fileName)
|
||||||
fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
|
fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
|
||||||
|
|
||||||
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
|
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
|
||||||
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
|
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
|
||||||
fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
|
fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
|
||||||
|
|
||||||
fclose(txtFile);
|
fclose(txtFile);
|
||||||
|
|
@ -1050,7 +984,7 @@ void ImageFormat(Image *image, int newFormat)
|
||||||
r = (unsigned char)(round(pixels[i].x*31.0f));
|
r = (unsigned char)(round(pixels[i].x*31.0f));
|
||||||
g = (unsigned char)(round(pixels[i].y*31.0f));
|
g = (unsigned char)(round(pixels[i].y*31.0f));
|
||||||
b = (unsigned char)(round(pixels[i].z*31.0f));
|
b = (unsigned char)(round(pixels[i].z*31.0f));
|
||||||
a = (pixels[i].w > ((float)ALPHA_THRESHOLD/255.0f)) ? 1 : 0;
|
a = (pixels[i].w > ((float)ALPHA_THRESHOLD/255.0f))? 1 : 0;
|
||||||
|
|
||||||
((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
|
((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
|
||||||
}
|
}
|
||||||
|
|
@ -1133,7 +1067,9 @@ void ImageFormat(Image *image, int newFormat)
|
||||||
if (image->mipmaps > 1)
|
if (image->mipmaps > 1)
|
||||||
{
|
{
|
||||||
image->mipmaps = 1;
|
image->mipmaps = 1;
|
||||||
|
#if defined(SUPPORT_IMAGE_MANIPULATION)
|
||||||
if (image->data != NULL) ImageMipmaps(image);
|
if (image->data != NULL) ImageMipmaps(image);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted");
|
else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted");
|
||||||
|
|
@ -1202,38 +1138,6 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
|
||||||
ImageFormat(image, prevFormat);
|
ImageFormat(image, prevFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crop image depending on alpha value
|
|
||||||
void ImageAlphaCrop(Image *image, float threshold)
|
|
||||||
{
|
|
||||||
Color *pixels = GetImageData(*image);
|
|
||||||
|
|
||||||
int xMin = 65536; // Define a big enough number
|
|
||||||
int xMax = 0;
|
|
||||||
int yMin = 65536;
|
|
||||||
int yMax = 0;
|
|
||||||
|
|
||||||
for (int y = 0; y < image->height; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < image->width; x++)
|
|
||||||
{
|
|
||||||
if (pixels[y*image->width + x].a > (unsigned char)(threshold*255.0f))
|
|
||||||
{
|
|
||||||
if (x < xMin) xMin = x;
|
|
||||||
if (x > xMax) xMax = x;
|
|
||||||
if (y < yMin) yMin = y;
|
|
||||||
if (y > yMax) yMax = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin };
|
|
||||||
|
|
||||||
free(pixels);
|
|
||||||
|
|
||||||
// Check for not empty image brefore cropping
|
|
||||||
if (!((xMax < xMin) || (yMax < yMin))) ImageCrop(image, crop);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Premultiply alpha channel
|
// Premultiply alpha channel
|
||||||
void ImageAlphaPremultiply(Image *image)
|
void ImageAlphaPremultiply(Image *image)
|
||||||
{
|
{
|
||||||
|
|
@ -1259,6 +1163,90 @@ void ImageAlphaPremultiply(Image *image)
|
||||||
|
|
||||||
|
|
||||||
#if defined(SUPPORT_IMAGE_MANIPULATION)
|
#if defined(SUPPORT_IMAGE_MANIPULATION)
|
||||||
|
// Load cubemap from image, multiple image cubemap layouts supported
|
||||||
|
TextureCubemap LoadTextureCubemap(Image image, int layoutType)
|
||||||
|
{
|
||||||
|
TextureCubemap cubemap = { 0 };
|
||||||
|
|
||||||
|
if (layoutType == CUBEMAP_AUTO_DETECT) // Try to automatically guess layout type
|
||||||
|
{
|
||||||
|
// Check image width/height to determine the type of cubemap provided
|
||||||
|
if (image.width > image.height)
|
||||||
|
{
|
||||||
|
if ((image.width/6) == image.height) { layoutType = CUBEMAP_LINE_HORIZONTAL; cubemap.width = image.width/6; }
|
||||||
|
else if ((image.width/4) == (image.height/3)) { layoutType = CUBEMAP_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
|
||||||
|
else if (image.width >= (int)((float)image.height*1.85f)) { layoutType = CUBEMAP_PANORAMA; cubemap.width = image.width/4; }
|
||||||
|
}
|
||||||
|
else if (image.height > image.width)
|
||||||
|
{
|
||||||
|
if ((image.height/6) == image.width) { layoutType = CUBEMAP_LINE_VERTICAL; cubemap.width = image.height/6; }
|
||||||
|
else if ((image.width/3) == (image.height/4)) { layoutType = CUBEMAP_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
|
||||||
|
}
|
||||||
|
|
||||||
|
cubemap.height = cubemap.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = cubemap.width;
|
||||||
|
|
||||||
|
if (layoutType != CUBEMAP_AUTO_DETECT)
|
||||||
|
{
|
||||||
|
//unsigned int dataSize = GetPixelDataSize(size, size, format);
|
||||||
|
//void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column
|
||||||
|
|
||||||
|
Image faces = { 0 }; // Vertical column image
|
||||||
|
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
|
||||||
|
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, size, size };
|
||||||
|
|
||||||
|
if (layoutType == CUBEMAP_LINE_VERTICAL)
|
||||||
|
{
|
||||||
|
faces = image;
|
||||||
|
for (int i = 0; i < 6; i++) faceRecs[i].y = size*i;
|
||||||
|
}
|
||||||
|
else if (layoutType == CUBEMAP_PANORAMA)
|
||||||
|
{
|
||||||
|
// TODO: Convert panorama image to square faces...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (layoutType == CUBEMAP_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = size*i;
|
||||||
|
else if (layoutType == CUBEMAP_CROSS_THREE_BY_FOUR)
|
||||||
|
{
|
||||||
|
faceRecs[0].x = size; faceRecs[0].y = size;
|
||||||
|
faceRecs[1].x = size; faceRecs[1].y = 3*size;
|
||||||
|
faceRecs[2].x = size; faceRecs[2].y = 0;
|
||||||
|
faceRecs[3].x = size; faceRecs[3].y = 2*size;
|
||||||
|
faceRecs[4].x = 0; faceRecs[4].y = size;
|
||||||
|
faceRecs[5].x = 2*size; faceRecs[5].y = size;
|
||||||
|
}
|
||||||
|
else if (layoutType == CUBEMAP_CROSS_FOUR_BY_THREE)
|
||||||
|
{
|
||||||
|
faceRecs[0].x = 2*size; faceRecs[0].y = size;
|
||||||
|
faceRecs[1].x = 0; faceRecs[1].y = size;
|
||||||
|
faceRecs[2].x = size; faceRecs[2].y = 0;
|
||||||
|
faceRecs[3].x = size; faceRecs[3].y = 2*size;
|
||||||
|
faceRecs[4].x = size; faceRecs[4].y = size;
|
||||||
|
faceRecs[5].x = 3*size; faceRecs[5].y = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
|
||||||
|
faces = GenImageColor(size, size*6, MAGENTA);
|
||||||
|
ImageFormat(&faces, image.format);
|
||||||
|
|
||||||
|
// TODO: Image formating does not work with compressed textures!
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size });
|
||||||
|
|
||||||
|
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
|
||||||
|
if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded.");
|
||||||
|
|
||||||
|
UnloadImage(faces);
|
||||||
|
}
|
||||||
|
else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected.");
|
||||||
|
|
||||||
|
return cubemap;
|
||||||
|
}
|
||||||
|
|
||||||
// Crop an image to area defined by a rectangle
|
// Crop an image to area defined by a rectangle
|
||||||
// NOTE: Security checks are performed in case rectangle goes out of bounds
|
// NOTE: Security checks are performed in case rectangle goes out of bounds
|
||||||
void ImageCrop(Image *image, Rectangle crop)
|
void ImageCrop(Image *image, Rectangle crop)
|
||||||
|
|
@ -1309,6 +1297,38 @@ void ImageCrop(Image *image, Rectangle crop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crop image depending on alpha value
|
||||||
|
void ImageAlphaCrop(Image *image, float threshold)
|
||||||
|
{
|
||||||
|
Color *pixels = GetImageData(*image);
|
||||||
|
|
||||||
|
int xMin = 65536; // Define a big enough number
|
||||||
|
int xMax = 0;
|
||||||
|
int yMin = 65536;
|
||||||
|
int yMax = 0;
|
||||||
|
|
||||||
|
for (int y = 0; y < image->height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < image->width; x++)
|
||||||
|
{
|
||||||
|
if (pixels[y*image->width + x].a > (unsigned char)(threshold*255.0f))
|
||||||
|
{
|
||||||
|
if (x < xMin) xMin = x;
|
||||||
|
if (x > xMax) xMax = x;
|
||||||
|
if (y < yMin) yMin = y;
|
||||||
|
if (y > yMax) yMax = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin };
|
||||||
|
|
||||||
|
free(pixels);
|
||||||
|
|
||||||
|
// Check for not empty image brefore cropping
|
||||||
|
if (!((xMax < xMin) || (yMax < yMin))) ImageCrop(image, crop);
|
||||||
|
}
|
||||||
|
|
||||||
// Resize and image to new size
|
// Resize and image to new size
|
||||||
// NOTE: Uses stb default scaling filters (both bicubic):
|
// NOTE: Uses stb default scaling filters (both bicubic):
|
||||||
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
|
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
|
||||||
|
|
@ -1611,7 +1631,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
|
||||||
if (palCount >= maxPaletteSize)
|
if (palCount >= maxPaletteSize)
|
||||||
{
|
{
|
||||||
i = image.width*image.height; // Finish palette get
|
i = image.width*image.height; // Finish palette get
|
||||||
printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize);
|
TraceLog(LOG_WARNING, "Image palette is greater than %i colors!", maxPaletteSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2146,7 +2166,6 @@ void ImageColorReplace(Image *image, Color color, Color replace)
|
||||||
}
|
}
|
||||||
#endif // SUPPORT_IMAGE_MANIPULATION
|
#endif // SUPPORT_IMAGE_MANIPULATION
|
||||||
|
|
||||||
#if defined(SUPPORT_IMAGE_GENERATION)
|
|
||||||
// Generate image: plain color
|
// Generate image: plain color
|
||||||
Image GenImageColor(int width, int height, Color color)
|
Image GenImageColor(int width, int height, Color color)
|
||||||
{
|
{
|
||||||
|
|
@ -2161,6 +2180,7 @@ Image GenImageColor(int width, int height, Color color)
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(SUPPORT_IMAGE_GENERATION)
|
||||||
// Generate image: vertical gradient
|
// Generate image: vertical gradient
|
||||||
Image GenImageGradientV(int width, int height, Color top, Color bottom)
|
Image GenImageGradientV(int width, int height, Color top, Color bottom)
|
||||||
{
|
{
|
||||||
|
|
@ -2211,7 +2231,7 @@ Image GenImageGradientH(int width, int height, Color left, Color right)
|
||||||
Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
|
Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
|
||||||
{
|
{
|
||||||
Color *pixels = (Color *)malloc(width*height*sizeof(Color));
|
Color *pixels = (Color *)malloc(width*height*sizeof(Color));
|
||||||
float radius = (width < height) ? (float)width/2.0f : (float)height/2.0f;
|
float radius = (width < height)? (float)width/2.0f : (float)height/2.0f;
|
||||||
|
|
||||||
float centerX = (float)width/2.0f;
|
float centerX = (float)width/2.0f;
|
||||||
float centerY = (float)height/2.0f;
|
float centerY = (float)height/2.0f;
|
||||||
|
|
@ -3173,12 +3193,14 @@ static int SaveKTX(Image image, const char *fileName)
|
||||||
{
|
{
|
||||||
KTXHeader ktxHeader;
|
KTXHeader ktxHeader;
|
||||||
|
|
||||||
// KTX identifier (v2.2)
|
// KTX identifier (v1.1)
|
||||||
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
|
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
|
||||||
//unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
|
//unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||||
|
|
||||||
|
const char ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
|
||||||
|
|
||||||
// Get the image header
|
// Get the image header
|
||||||
strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
|
strncpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature
|
||||||
ktxHeader.endianness = 0;
|
ktxHeader.endianness = 0;
|
||||||
ktxHeader.glType = 0; // Obtained from image.format
|
ktxHeader.glType = 0; // Obtained from image.format
|
||||||
ktxHeader.glTypeSize = 1;
|
ktxHeader.glTypeSize = 1;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user