Merge pull request #2 from raysan5/master

merged raylib-master
This commit is contained in:
Jak 2019-02-24 21:57:31 +00:00 committed by GitHub
commit 44c2df3c12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 545 additions and 422 deletions

1
.gitignore vendored
View File

@ -49,6 +49,7 @@ ipch/
# Ignore compiled binaries
*.o
*.exe
*.a
!raylib.rc.o
# Ignore all examples files

View File

@ -707,7 +707,7 @@ bool WindowShouldClose(void)
{
#if defined(PLATFORM_WEB)
// 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
// 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!
@ -871,7 +871,7 @@ void *GetWindowHandle(void)
{
#if defined(_WIN32)
// NOTE: Returned handle is: void *HWND (windows.h)
return glfwGetWin32Window(window);
return glfwGetWin32Window(window);
#elif defined(__linux__)
// NOTE: Returned handle is: unsigned long Window (X.h)
// typedef unsigned long XID;
@ -1228,7 +1228,7 @@ void BeginTextureMode(RenderTexture2D target)
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
//rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
// Setup current width/height for proper aspect ratio
// calculation when using BeginMode3D()
currentWidth = target.texture.width;
@ -1254,6 +1254,10 @@ void EndTextureMode(void)
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
// Reset current screen size
currentWidth = GetScreenWidth();
currentHeight = GetScreenHeight();
}
// Returns a ray trace from mouse position
@ -1426,11 +1430,11 @@ Vector3 ColorToHSV(Color color)
Vector3 hsv = { 0.0f, 0.0f, 0.0f };
float min, max, delta;
min = rgb.x < rgb.y ? rgb.x : rgb.y;
min = min < rgb.z ? min : rgb.z;
min = rgb.x < rgb.y? rgb.x : rgb.y;
min = min < rgb.z? min : rgb.z;
max = rgb.x > rgb.y ? rgb.x : rgb.y;
max = max > rgb.z ? max : rgb.z;
max = rgb.x > rgb.y? rgb.x : rgb.y;
max = max > rgb.z? max : rgb.z;
hsv.z = max; // Value
delta = max - min;
@ -1481,25 +1485,25 @@ Color ColorFromHSV(Vector3 hsv)
// Red channel
float k = fmod((5.0f + h/60.0f), 6);
float t = 4.0f - k;
k = (t < k) ? t : k;
k = (k < 1) ? k : 1;
k = (k > 0) ? k : 0;
k = (t < k)? t : k;
k = (k < 1)? k : 1;
k = (k > 0)? k : 0;
color.r = (v - v*s*k)*255;
// Green channel
k = fmod((3.0f + h/60.0f), 6);
t = 4.0f - k;
k = (t < k) ? t : k;
k = (k < 1) ? k : 1;
k = (k > 0) ? k : 0;
k = (t < k)? t : k;
k = (k < 1)? k : 1;
k = (k > 0)? k : 0;
color.g = (v - v*s*k)*255;
// Blue channel
k = fmod((1.0f + h/60.0f), 6);
t = 4.0f - k;
k = (t < k) ? t : k;
k = (k < 1) ? k : 1;
k = (k > 0) ? k : 0;
k = (t < k)? t : k;
k = (k < 1)? k : 1;
k = (k > 0)? k : 0;
color.b = (v - v*s*k)*255;
return color;
@ -1673,7 +1677,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
// NOTE: strrchr() returns a pointer to the last occurrence of character
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...
{
@ -1913,14 +1917,13 @@ void OpenURL(const char *url)
char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char));
#if defined(_WIN32)
sprintf(cmd, "explorer '%s'", url);
sprintf(cmd, "explorer %s", url);
#elif defined(__linux__)
sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
#elif defined(__APPLE__)
sprintf(cmd, "open '%s'", url);
#endif
system(cmd);
free(cmd);
}
}
@ -2210,7 +2213,7 @@ void SetMouseOffset(int offsetX, int offsetY)
// NOTE: Useful when rendering to different size targets
void SetMouseScale(float scaleX, float scaleY)
{
mouseScale = (Vector2){ scaleX, scaleY };
mouseScale = (Vector2){ scaleX, scaleY };
}
// Returns mouse wheel movement Y
@ -3188,7 +3191,7 @@ static void PollInputEvents(void)
// Poll Events (registered events)
// 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
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 };
#if defined(PLATFORM_ANDROID)
strcpy(path, internalDataPath);
strcat(path, TextFormat("/screenrec%03i.gif", screenshotCounter));
strcat(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
#else
strcpy(path, TextFormat("/screenrec%03i.gif", screenshotCounter));
strcpy(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
#endif
// 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,
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)
{
@ -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",
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);
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
TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device,
worker->isMouse ? "mouse " : "",
worker->isMultitouch ? "multitouch " : "",
worker->isTouch ? "touchscreen " : "",
worker->isGamepad ? "gamepad " : "",
worker->isKeyboard ? "keyboard " : "");
worker->isMouse? "mouse " : "",
worker->isMultitouch? "multitouch " : "",
worker->isTouch? "touchscreen " : "",
worker->isGamepad? "gamepad " : "",
worker->isKeyboard? "keyboard " : "");
// Create a thread for this device
int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);

51
src/external/cgltf.h vendored
View File

@ -1,6 +1,49 @@
/**
* 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.
*
* 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__
#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_world(const cgltf_node* node, cgltf_float* out_matrix);
#ifdef __cplusplus
}
#endif
#endif /* #ifndef CGLTF_H_INCLUDED__ */
/*
@ -4266,10 +4313,6 @@ static void jsmn_init(jsmn_parser *parser) {
#endif /* #ifdef CGLTF_IMPLEMENTATION */
#ifdef __cplusplus
}
#endif
/* cgltf is distributed under MIT license:
*
* Copyright (c) 2018 Johannes Kuhlmann

Binary file not shown.

View File

@ -116,7 +116,7 @@ void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
{
if (rlCheckBufferLimit(2*36)) rlglDraw();
rlPushMatrix();
rlTranslatef(center.x, center.y, center.z);
rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
@ -140,7 +140,7 @@ void DrawCube(Vector3 position, float width, float height, float length, Color c
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if (rlCheckBufferLimit(36)) rlglDraw();
rlPushMatrix();
@ -221,7 +221,7 @@ void DrawCubeWires(Vector3 position, float width, float height, float length, Co
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if (rlCheckBufferLimit(36)) rlglDraw();
rlPushMatrix();
@ -626,7 +626,7 @@ Model LoadModel(const char *fileName)
Model LoadModelFromMesh(Mesh mesh)
{
Model model = { 0 };
model.mesh = mesh;
model.transform = MatrixIdentity();
model.material = LoadMaterialDefault();
@ -655,12 +655,16 @@ Mesh LoadMesh(const char *fileName)
TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName);
#endif
#if defined(SUPPORT_MESH_GENERATION)
if (mesh.vertexCount == 0)
{
TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!");
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)
#endif
return mesh;
}
@ -675,11 +679,11 @@ void UnloadMesh(Mesh *mesh)
void ExportMesh(Mesh mesh, const char *fileName)
{
bool success = false;
if (IsFileExtension(fileName, ".obj"))
{
FILE *objFile = fopen(fileName, "wt");
fprintf(objFile, "# //////////////////////////////////////////////////////////////////////////////////\n");
fprintf(objFile, "# // //\n");
fprintf(objFile, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized //\n");
@ -692,33 +696,33 @@ void ExportMesh(Mesh mesh, const char *fileName)
fprintf(objFile, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount);
fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount);
fprintf(objFile, "g mesh\n");
for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
{
fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
}
for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
{
fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
}
for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
{
fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
}
for (int i = 0; i < mesh.triangleCount; i += 3)
{
fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2);
}
fprintf(objFile, "\n");
fclose(objFile);
success = true;
}
else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export mesh vertex data
@ -733,7 +737,7 @@ Mesh GenMeshPoly(int sides, float radius)
{
Mesh mesh = { 0 };
int vertexCount = sides*3;
// Vertices definition
Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
for (int i = 0, v = 0; i < 360; i += 360/sides, v += 3)
@ -741,13 +745,13 @@ Mesh GenMeshPoly(int sides, float radius)
vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
vertices[v + 1] = (Vector3){ sinf(DEG2RAD*i)*radius, 0.0f, cosf(DEG2RAD*i)*radius };
vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(i + 360/sides))*radius, 0.0f, cosf(DEG2RAD*(i + 360/sides))*radius };
}
}
// Normals definition
Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
// TexCoords definition
// TexCoords definition
Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2));
for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f };
@ -756,7 +760,7 @@ Mesh GenMeshPoly(int sides, float radius)
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
// Mesh vertices position array
for (int i = 0; i < mesh.vertexCount; i++)
{
@ -764,14 +768,14 @@ Mesh GenMeshPoly(int sides, float radius)
mesh.vertices[3*i + 1] = vertices[i].y;
mesh.vertices[3*i + 2] = vertices[i].z;
}
// Mesh texcoords array
for (int i = 0; i < mesh.vertexCount; i++)
{
mesh.texcoords[2*i] = texcoords[i].x;
mesh.texcoords[2*i + 1] = texcoords[i].y;
}
// Mesh normals array
for (int i = 0; i < mesh.vertexCount; i++)
{
@ -779,14 +783,14 @@ Mesh GenMeshPoly(int sides, float radius)
mesh.normals[3*i + 1] = normals[i].y;
mesh.normals[3*i + 2] = normals[i].z;
}
free(vertices);
free(normals);
free(texcoords);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -799,7 +803,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
#if defined(CUSTOM_MESH_GEN_PLANE)
resX++;
resZ++;
// Vertices definition
int vertexCount = resX*resZ; // vertices get reused for the faces
@ -820,7 +824,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
// TexCoords definition
// TexCoords definition
Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2));
for (int v = 0; v < resZ; v++)
{
@ -843,7 +847,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
triangles[t++] = i + 1;
triangles[t++] = i;
triangles[t++] = i + resX;
triangles[t++] = i + resX;
triangles[t++] = i + resX + 1;
triangles[t++] = i + 1;
}
@ -854,7 +858,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.indices = (unsigned short *)malloc(mesh.triangleCount*3*sizeof(unsigned short));
// Mesh vertices position array
for (int i = 0; i < mesh.vertexCount; i++)
{
@ -862,14 +866,14 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
mesh.vertices[3*i + 1] = vertices[i].y;
mesh.vertices[3*i + 2] = vertices[i].z;
}
// Mesh texcoords array
for (int i = 0; i < mesh.vertexCount; i++)
{
mesh.texcoords[2*i] = texcoords[i].x;
mesh.texcoords[2*i + 1] = texcoords[i].y;
}
// Mesh normals array
for (int i = 0; i < mesh.vertexCount; i++)
{
@ -877,22 +881,22 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
mesh.normals[3*i + 1] = normals[i].y;
mesh.normals[3*i + 2] = normals[i].z;
}
// Mesh indices array initialization
for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
free(vertices);
free(normals);
free(texcoords);
free(triangles);
#else // Use par_shapes library to generate plane mesh
par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ); // No normals/texcoords generated!!!
par_shapes_scale(plane, width, length, 1.0f);
par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_translate(plane, -width/2, 0.0f, length/2);
mesh.vertices = (float *)malloc(plane->ntriangles*3*3*sizeof(float));
mesh.texcoords = (float *)malloc(plane->ntriangles*3*2*sizeof(float));
mesh.normals = (float *)malloc(plane->ntriangles*3*3*sizeof(float));
@ -905,11 +909,11 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
}
@ -918,7 +922,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
#endif
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -956,7 +960,7 @@ Mesh GenMeshCube(float width, float height, float length)
-width/2, height/2, length/2,
-width/2, height/2, -length/2
};
float texcoords[] = {
0.0f, 0.0f,
1.0f, 0.0f,
@ -983,7 +987,7 @@ Mesh GenMeshCube(float width, float height, float length)
1.0f, 1.0f,
0.0f, 1.0f
};
float normals[] = {
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
@ -1013,15 +1017,15 @@ Mesh GenMeshCube(float width, float height, float length)
mesh.vertices = (float *)malloc(24*3*sizeof(float));
memcpy(mesh.vertices, vertices, 24*3*sizeof(float));
mesh.texcoords = (float *)malloc(24*2*sizeof(float));
memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float));
mesh.normals = (float *)malloc(24*3*sizeof(float));
memcpy(mesh.normals, normals, 24*3*sizeof(float));
mesh.indices = (unsigned short *)malloc(36*sizeof(unsigned short));
int k = 0;
// Indices can be initialized right now
@ -1036,10 +1040,10 @@ Mesh GenMeshCube(float width, float height, float length)
k++;
}
mesh.vertexCount = 24;
mesh.triangleCount = 12;
#else // Use par_shapes library to generate cube mesh
/*
// Platonic solids:
@ -1053,11 +1057,11 @@ par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
// NOTE: No normals/texcoords generated by default
par_shapes_mesh *cube = par_shapes_create_cube();
cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
par_shapes_scale(cube, width, height, length);
par_shapes_translate(cube, -width/2, 0.0f, -length/2);
par_shapes_compute_normals(cube);
mesh.vertices = (float *)malloc(cube->ntriangles*3*3*sizeof(float));
mesh.texcoords = (float *)malloc(cube->ntriangles*3*2*sizeof(float));
mesh.normals = (float *)malloc(cube->ntriangles*3*3*sizeof(float));
@ -1070,11 +1074,11 @@ par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
}
@ -1083,7 +1087,7 @@ par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
#endif
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1095,8 +1099,8 @@ RLAPI Mesh GenMeshSphere(float radius, int rings, int slices)
par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
par_shapes_scale(sphere, radius, radius, radius);
// NOTE: Soft normals are computed internally
// NOTE: Soft normals are computed internally
mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float));
mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
@ -1109,19 +1113,19 @@ RLAPI Mesh GenMeshSphere(float radius, int rings, int slices)
mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
}
par_shapes_free_mesh(sphere);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1133,8 +1137,8 @@ RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices)
par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
par_shapes_scale(sphere, radius, radius, radius);
// NOTE: Soft normals are computed internally
// NOTE: Soft normals are computed internally
mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float));
mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float));
@ -1147,19 +1151,19 @@ RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices)
mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
}
par_shapes_free_mesh(sphere);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1171,7 +1175,7 @@ Mesh GenMeshCylinder(float radius, float height, int slices)
// Instance a cylinder that sits on the Z=0 plane using the given tessellation
// levels across the UV domain. Think of "slices" like a number of pizza
// slices, and "stacks" like a number of stacked rings.
// slices, and "stacks" like a number of stacked rings.
// Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
par_shapes_scale(cylinder, radius, radius, height);
@ -1183,16 +1187,16 @@ Mesh GenMeshCylinder(float radius, float height, int slices)
for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_translate(capTop, 0, height, 0);
// Generate an orientable disk shape (bottom cap)
par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_merge_and_free(cylinder, capTop);
par_shapes_merge_and_free(cylinder, capBottom);
mesh.vertices = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float));
mesh.texcoords = (float *)malloc(cylinder->ntriangles*3*2*sizeof(float));
mesh.normals = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float));
@ -1205,19 +1209,19 @@ Mesh GenMeshCylinder(float radius, float height, int slices)
mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
}
par_shapes_free_mesh(cylinder);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1229,7 +1233,7 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
if (radius > 1.0f) radius = 1.0f;
else if (radius < 0.1f) radius = 0.1f;
// Create a donut that sits on the Z=0 plane with the specified inner radius
// The outer radius can be controlled with par_shapes_scale
par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
@ -1247,19 +1251,19 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
}
par_shapes_free_mesh(torus);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1268,7 +1272,7 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
{
Mesh mesh = { 0 };
if (radius > 3.0f) radius = 3.0f;
else if (radius < 0.5f) radius = 0.5f;
@ -1287,19 +1291,19 @@ Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
}
par_shapes_free_mesh(knot);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1407,7 +1411,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
}
free(pixels);
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
@ -1767,9 +1771,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
free(mapTexcoords);
free(cubicmapPixels); // Free image pixel data
// Upload vertex data to GPU (static mesh)
rlLoadMesh(&mesh, false);
rlLoadMesh(&mesh, false);
return mesh;
}
@ -1817,7 +1821,7 @@ void UnloadMaterial(Material material)
// Unload loaded texture maps (avoid unloading default texture, managed by raylib)
for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
{
if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id);
if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id);
}
}
@ -1838,7 +1842,7 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
// Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
@ -2033,7 +2037,7 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi
if (distance < sphereRadius) collisionDistance = vector + sqrtf(d);
else collisionDistance = vector - sqrtf(d);
// Calculate collision point
Vector3 cPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, collisionDistance));
@ -2093,7 +2097,7 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model)
b = vertdata[i*3 + 1];
c = vertdata[i*3 + 2];
}
a = Vector3Transform(a, model->transform);
b = Vector3Transform(b, model->transform);
c = Vector3Transform(c, model->transform);
@ -2227,7 +2231,7 @@ void MeshTangents(Mesh *mesh)
{
if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float));
else TraceLog(LOG_WARNING, "Mesh tangents already exist");
Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3));
Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3));
@ -2256,11 +2260,11 @@ void MeshTangents(Mesh *mesh)
float t2 = uv3.y - uv1.y;
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 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
tan1[i + 0] = sdir;
tan1[i + 1] = sdir;
tan1[i + 2] = sdir;
@ -2289,13 +2293,13 @@ void MeshTangents(Mesh *mesh)
mesh->tangents[i*4 + 0] = tangent.x;
mesh->tangents[i*4 + 1] = tangent.y;
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
}
free(tan1);
free(tan2);
TraceLog(LOG_INFO, "Tangents computed for mesh");
}
@ -2307,8 +2311,8 @@ void MeshBinormals(Mesh *mesh)
Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 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];
// TODO: Register computed binormal in mesh->binormal ?
// TODO: Register computed binormal in mesh->binormal?
// Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW);
}
}
@ -2635,7 +2639,7 @@ static Material LoadMTL(const char *fileName)
} break;
case 'e': // Ke float float float Emmisive color (RGB)
{
// TODO: Support Ke ?
// TODO: Support Ke?
} break;
default: break;
}
@ -2736,25 +2740,25 @@ static Material LoadMTL(const char *fileName)
static Mesh LoadIQM(const char *fileName)
{
Mesh mesh = { 0 };
// TODO: Load IQM file
return mesh;
}
}
#endif
#if defined(SUPPORT_FILEFORMAT_GLTF)
// Load GLTF mesh data
// Load glTF mesh data
static Mesh LoadGLTF(const char *fileName)
{
Mesh mesh = { 0 };
// GLTF file loading
// glTF file loading
FILE *gltfFile = fopen(fileName, "rb");
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;
}
@ -2764,25 +2768,31 @@ static Mesh LoadGLTF(const char *fileName)
void *buffer = malloc(size);
fread(buffer, size, 1, gltfFile);
fclose(gltfFile);
// GLTF data loading
// glTF data loading
cgltf_options options = {0};
cgltf_data data;
cgltf_result result = cgltf_parse(&options, buffer, size, &data);
free(buffer);
if (result == cgltf_result_success)
{
printf("Type: %u\n", data.file_type);
printf("Version: %d\n", data.version);
printf("Meshes: %lu\n", data.meshes_count);
}
else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName);
free(buffer);
cgltf_free(&data);
return mesh;
// 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);
}
else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName);
return mesh;
}
#endif

View File

@ -712,11 +712,13 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
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
// 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);
}
@ -767,7 +769,11 @@ Wave LoadWave(const char *fileName)
{
Wave wave = { 0 };
#if defined(SUPPORT_FILEFORMAT_WAV)
if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_OGG)
else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
#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.
// 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 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;
#if defined(SUPPORT_FILEFORMAT_WAV)
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
#else
if (false) {}
#endif
else if (IsFileExtension(fileName, ".raw"))
{
// Export raw sample data (without header)
@ -938,7 +948,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
// Write byte data as hexadecimal text
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]);
fclose(txtFile);
@ -989,8 +999,8 @@ void SetSoundPitch(Sound sound, float pitch)
// Convert wave data to desired format
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 formatOut = (( sampleSize == 8) ? mal_format_u8 : (( 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_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));
bool musicLoaded = true;
#if defined(SUPPORT_FILEFORMAT_OGG)
if (IsFileExtension(fileName, ".ogg"))
{
// 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);
}
}
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (IsFileExtension(fileName, ".flac"))
{
@ -1202,7 +1216,11 @@ Music LoadMusicStream(const char *fileName)
if (!musicLoaded)
{
#if defined(SUPPORT_FILEFORMAT_OGG)
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
#endif
@ -1229,10 +1247,14 @@ Music LoadMusicStream(const char *fileName)
void UnloadMusicStream(Music music)
{
if (music == NULL) return;
CloseAudioStream(music->stream);
#if defined(SUPPORT_FILEFORMAT_OGG)
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
#endif
@ -1291,13 +1313,15 @@ void ResumeMusicStream(Music music)
void StopMusicStream(Music music)
{
if (music == NULL) return;
StopAudioStream(music->stream);
// Restart music context
switch (music->ctxType)
{
#if defined(SUPPORT_FILEFORMAT_OGG)
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
#endif
@ -1321,7 +1345,7 @@ void StopMusicStream(Music music)
void UpdateMusicStream(Music music)
{
if (music == NULL) return;
bool streamEnding = false;
unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2;
@ -1339,12 +1363,14 @@ void UpdateMusicStream(Music music)
// TODO: Really don't like ctxType thingy...
switch (music->ctxType)
{
#if defined(SUPPORT_FILEFORMAT_OGG)
case MUSIC_AUDIO_OGG:
{
// 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);
} break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC:
{
@ -1369,21 +1395,21 @@ void UpdateMusicStream(Music music)
} break;
#endif
#if defined(SUPPORT_FILEFORMAT_MOD)
case MUSIC_MODULE_MOD:
case MUSIC_MODULE_MOD:
{
// NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2
jar_mod_fillbuffer(&music->ctxMod, (short *)pcm, samplesCount/2, 0);
jar_mod_fillbuffer(&music->ctxMod, (short *)pcm, samplesCount/2, 0);
} break;
#endif
default: break;
}
UpdateAudioStream(music->stream, pcm, samplesCount);
if ((music->ctxType == MUSIC_MODULE_XM) || (music->ctxType == MUSIC_MODULE_MOD))
{
if (samplesCount > 1) music->samplesLeft -= samplesCount/2;
else music->samplesLeft -= samplesCount;
if (samplesCount > 1) music->samplesLeft -= samplesCount/2;
else music->samplesLeft -= samplesCount;
}
else music->samplesLeft -= samplesCount;
@ -1451,7 +1477,7 @@ void SetMusicLoopCount(Music music, int count)
float GetMusicTimeLength(Music music)
{
float totalSeconds = 0.0f;
if (music != NULL) totalSeconds = (float)music->totalSamples/(music->stream.sampleRate*music->stream.channels);
return totalSeconds;
@ -1487,7 +1513,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
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.
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.
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;
}
@ -1542,7 +1568,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
else
{
// 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;
@ -1745,7 +1771,7 @@ static Wave LoadWAV(const char *fileName)
// NOTE: subChunkSize comes in bytes, we need to translate it to number of samples
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_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);
}
@ -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.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;
}
@ -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.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;
}

View File

@ -356,7 +356,7 @@ typedef unsigned char byte;
LOC_MAP_PREFILTER,
LOC_MAP_BRDF
} ShaderLocationIndex;
// Shader uniform data types
typedef enum {
UNIFORM_FLOAT = 0,
@ -775,8 +775,8 @@ typedef struct DrawCall {
#endif
"void main() \n"
"{ \n"
" vec2 lensCenter = fragTexCoord.x < 0.5 ? leftLensCenter : rightLensCenter; \n"
" vec2 screenCenter = fragTexCoord.x < 0.5 ? leftScreenCenter : rightScreenCenter; \n"
" vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; \n"
" vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; \n"
" vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \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"
@ -993,14 +993,14 @@ void rlPushMatrix(void)
// Pop lattest inserted matrix from stack
void rlPopMatrix(void)
{
{
if (stackCounter > 0)
{
Matrix mat = stack[stackCounter - 1];
*currentMatrix = mat;
stackCounter--;
}
if ((stackCounter == 0) && (currentMatrixMode == RL_MODELVIEW))
{
currentMatrix = &modelview;
@ -1136,12 +1136,12 @@ void rlEnd(void)
// TODO: System could be improved (a bit) just storing every draw alignment value
// and adding it to vertexOffset on drawing... maybe in a future...
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);
// Make sure vertexCount is the same for vertices, texcoords, colors and normals
// NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls
// Make sure colors count match vertex count
if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].cCounter)
{
@ -1156,7 +1156,7 @@ void rlEnd(void)
vertexData[currentBuffer].cCounter++;
}
}
// Make sure texcoords count match vertex count
if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].tcCounter)
{
@ -1194,10 +1194,10 @@ void rlEnd(void)
void rlVertex3f(float x, float y, float z)
{
Vector3 vec = { x, y, z };
// Transform provided vector if required
if (useTransformMatrix) vec = Vector3Transform(vec, transformMatrix);
// Verify that MAX_BATCH_ELEMENTS limit not reached
if (vertexData[currentBuffer].vCounter < (MAX_BATCH_ELEMENTS*4))
{
@ -1233,7 +1233,7 @@ void rlTexCoord2f(float x, float y)
}
// Define one vertex (normal)
// NOTE: Normals limited to TRIANGLES only ?
// NOTE: Normals limited to TRIANGLES only?
void rlNormal3f(float x, float y, float z)
{
// TODO: Normals usage...
@ -1499,7 +1499,7 @@ void rlglInit(int width, int height)
//for (int i = 0; i < numComp; i++) TraceLog(LOG_INFO, "Supported compressed format: 0x%x", format[i]);
// NOTE: We don't need that much data on screen... right now...
// TODO: Automatize extensions loading using rlLoadExtensions() and GLAD
// Actually, when rlglInit() is called in InitWindow() in core.c,
// OpenGL required extensions have already been loaded (PLATFORM_DESKTOP)
@ -1512,7 +1512,7 @@ void rlglInit(int width, int height)
// NOTE: On OpenGL 3.3 VAO and NPOT are supported by default
vaoSupported = true;
// Multiple texture extensions supported by default
texNPOTSupported = true;
texFloatSupported = true;
@ -1585,11 +1585,11 @@ void rlglInit(int width, int height)
// Check texture float support
if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) texFloatSupported = true;
// Check depth texture support
if ((strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) ||
(strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0)) texDepthSupported = true;
if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) maxDepthBits = 24;
if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) maxDepthBits = 32;
#endif
@ -1648,8 +1648,8 @@ void rlglInit(int width, int height)
if (debugMarkerSupported) TraceLog(LOG_INFO, "[EXTENSION] Debug Marker supported");
// Initialize buffers, default shaders and default textures
//----------------------------------------------------------
@ -1666,7 +1666,7 @@ void rlglInit(int width, int height)
// Init default vertex arrays buffers
LoadBuffersDefault();
// Init transformations matrix accumulator
transformMatrix = MatrixIdentity();
@ -1995,9 +1995,9 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB
{
unsigned int id = 0;
unsigned int glInternalFormat = GL_DEPTH_COMPONENT16;
if ((bits != 16) && (bits != 24) && (bits != 32)) bits = 16;
if (bits == 24)
{
#if defined(GRAPHICS_API_OPENGL_33)
@ -2006,7 +2006,7 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB
if (maxDepthBits >= 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES;
#endif
}
if (bits == 32)
{
#if defined(GRAPHICS_API_OPENGL_33)
@ -2021,7 +2021,7 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
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);
@ -2036,10 +2036,10 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB
glGenRenderbuffers(1, &id);
glBindRenderbuffer(GL_RENDERBUFFER, id);
glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
return id;
}
@ -2053,7 +2053,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format)
glGenTextures(1, &cubemapId);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId);
unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
@ -2084,7 +2084,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format)
#endif
}
}
// Set cubemap texture sampling parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -2178,14 +2178,14 @@ void rlUnloadTexture(unsigned int id)
RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depthBits, bool useDepthTexture)
{
RenderTexture2D target = { 0 };
if (useDepthTexture && texDepthSupported) target.depthTexture = true;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Create the framebuffer object
glGenFramebuffers(1, &target.id);
glBindFramebuffer(GL_FRAMEBUFFER, target.id);
// Create fbo color texture attachment
//-----------------------------------------------------------------------------------------------------
if ((format != -1) && (format < COMPRESSED_DXT1_RGB))
@ -2198,7 +2198,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth
target.texture.mipmaps = 1;
}
//-----------------------------------------------------------------------------------------------------
// Create fbo depth renderbuffer/texture
//-----------------------------------------------------------------------------------------------------
if (depthBits > 0)
@ -2206,11 +2206,11 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth
target.depth.id = rlLoadTextureDepth(width, height, depthBits, !useDepthTexture);
target.depth.width = width;
target.depth.height = height;
target.depth.format = 19; //DEPTH_COMPONENT_24BIT ?
target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
target.depth.mipmaps = 1;
}
//-----------------------------------------------------------------------------------------------------
// Attach color texture and depth renderbuffer to FBO
//-----------------------------------------------------------------------------------------------------
rlRenderTextureAttach(target, target.texture.id, 0); // COLOR attachment
@ -2235,12 +2235,12 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy
glBindFramebuffer(GL_FRAMEBUFFER, target.id);
if (attachType == 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0);
else if (attachType == 1)
else if (attachType == 1)
{
if (target.depthTexture) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, id, 0);
else glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, id);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
@ -2248,7 +2248,7 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy
bool rlRenderTextureComplete(RenderTexture target)
{
glBindFramebuffer(GL_FRAMEBUFFER, target.id);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
@ -2264,9 +2264,9 @@ bool rlRenderTextureComplete(RenderTexture target)
default: break;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return (status == GL_FRAMEBUFFER_COMPLETE);
}
@ -2349,7 +2349,7 @@ void rlLoadMesh(Mesh *mesh, bool dynamic)
TraceLog(LOG_WARNING, "Trying to re-load an already loaded mesh");
return;
}
mesh->vaoId = 0; // Vertex Array Object
mesh->vboId[0] = 0; // Vertex positions VBO
mesh->vboId[1] = 0; // Vertex texcoords VBO
@ -2766,7 +2766,7 @@ unsigned char *rlReadScreenPixels(int width, int height)
for (int x = 0; x < (width*4); x++)
{
imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line
// Set alpha component value to 255 (no trasparent image retrieval)
// NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it!
if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255;
@ -2822,7 +2822,7 @@ void *rlReadTexturePixels(Texture2D texture)
// We are using Option 1, just need to care for texture format on retrieval
// NOTE: This behaviour could be conditioned by graphic driver...
RenderTexture2D fbo = rlLoadRenderTexture(texture.width, texture.height, UNCOMPRESSED_R8G8B8A8, 16, false);
glBindFramebuffer(GL_FRAMEBUFFER, fbo.id);
glBindTexture(GL_TEXTURE_2D, 0);
@ -2836,7 +2836,7 @@ void *rlReadTexturePixels(Texture2D texture)
// Get OpenGL internal formats and data type from our texture format
unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType);
// NOTE: We read data as RGBA because FBO texture is configured as RGBA, despite binding a RGB texture...
glReadPixels(0, 0, texture.width, texture.height, glFormat, glType, pixels);
@ -3064,7 +3064,7 @@ void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int unifo
case UNIFORM_SAMPLER2D: glUniform1iv(uniformLoc, count, (int *)value); break;
default: TraceLog(LOG_WARNING, "Shader uniform could not be set data type not recognized");
}
//glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set
#endif
}
@ -3143,7 +3143,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size)
// NOTE: Faces are stored as 32 bit floating point values
glGenTextures(1, &cubemap.id);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id);
for (unsigned int i = 0; i < 6; i++)
for (unsigned int i = 0; i < 6; i++)
{
#if defined(GRAPHICS_API_OPENGL_33)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
@ -3151,7 +3151,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size)
if (texFloatSupported) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL);
#endif
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#if defined(GRAPHICS_API_OPENGL_33)
@ -3231,7 +3231,7 @@ Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
@ -3309,7 +3309,7 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
@ -3417,7 +3417,7 @@ Texture2D GenTextureBRDF(Shader shader, int size)
// Unbind framebuffer and textures
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Unload framebuffer but keep color texture
glDeleteRenderbuffers(1, &rbo);
glDeleteFramebuffers(1, &fbo);
@ -3464,7 +3464,7 @@ void EndBlendMode(void)
void BeginScissorMode(int x, int y, int width, int height)
{
rlglDraw(); // Force drawing elements
glEnable(GL_SCISSOR_TEST);
glScissor(x, screenHeight - (y + height), width, height);
}
@ -3473,7 +3473,7 @@ void BeginScissorMode(int x, int y, int width, int height)
void EndScissorMode(void)
{
rlglDraw(); // Force drawing elements
glDisable(GL_SCISSOR_TEST);
}
@ -4050,7 +4050,7 @@ static void LoadBuffersDefault(void)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW);
#endif
}
TraceLog(LOG_INFO, "Internal buffers uploaded successfully (GPU)");
// Unbind the current VAO
@ -4073,7 +4073,7 @@ static void UpdateBuffersDefault(void)
glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].vertices);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer
// Texture coordinates buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].texcoords);
@ -4083,13 +4083,13 @@ static void UpdateBuffersDefault(void)
glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].colors);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer
// NOTE: glMapBuffer() causes sync issue.
// If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job.
// If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job.
// To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer().
// If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new
// allocated pointer immediately even if GPU is still working with the previous data.
// Another option: map the buffer object into client's memory
// Probably this code could be moved somewhere else...
// vertexData[currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
@ -4135,7 +4135,7 @@ static void DrawBuffersDefault(void)
glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0);
// NOTE: Additional map textures not considered for default buffers drawing
int vertexOffset = 0;
if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId);
@ -4160,7 +4160,7 @@ static void DrawBuffersDefault(void)
}
glActiveTexture(GL_TEXTURE0);
for (int i = 0; i < drawsCounter; i++)
{
glBindTexture(GL_TEXTURE_2D, draws[i].textureId);
@ -4170,7 +4170,7 @@ static void DrawBuffersDefault(void)
{
#if defined(GRAPHICS_API_OPENGL_33)
// We need to define the number of indices to be processed: quadsCount*6
// NOTE: The final parameter tells the GPU the offset in bytes from the
// NOTE: The final parameter tells the GPU the offset in bytes from the
// start of the index buffer to the location of the first index to process
glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6));
#elif defined(GRAPHICS_API_OPENGL_ES2)
@ -4216,7 +4216,7 @@ static void DrawBuffersDefault(void)
}
drawsCounter = 1;
// Change to next buffer in the list
currentBuffer++;
if (currentBuffer >= MAX_BATCH_BUFFERING) currentBuffer = 0;
@ -4369,14 +4369,14 @@ static void GenDrawCube(void)
static VrStereoConfig SetStereoConfig(VrDeviceInfo hmd, Shader distortion)
{
VrStereoConfig config = { 0 };
// Initialize framebuffer and textures for stereo rendering
// NOTE: Screen size should match HMD aspect ratio
config.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false);
// Assign distortion shader
config.distortionShader = distortion;
// Compute aspect ratio
float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution;
@ -4442,7 +4442,7 @@ static VrStereoConfig SetStereoConfig(VrDeviceInfo hmd, Shader distortion)
SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4);
SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4);
#endif
return config;
}

View File

@ -132,7 +132,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
rlPushMatrix();
rlTranslatef((float)startPos.x, (float)startPos.y, 0.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);
rlColor4ub(color.r, color.g, color.b, color.a);
@ -143,7 +143,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(0.0f, thick);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(d, thick);
@ -187,7 +187,7 @@ void DrawCircle(int centerX, int centerY, float radius, Color color)
void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, Color color)
{
#define CIRCLE_SECTOR_LENGTH 10
#if defined(SUPPORT_QUADS_DRAW_MODE)
if (rlCheckBufferLimit(4*((360/CIRCLE_SECTOR_LENGTH)/2))) rlglDraw();
@ -307,10 +307,10 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0.0f, 0.0f);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(0.0f, rec.height);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(rec.width, rec.height);
@ -644,7 +644,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
if (dy <= (rec.height/2.0f)) { return true; }
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
return (cornerDistanceSq <= (radius*radius));
}
@ -742,7 +742,7 @@ static Texture2D GetShapesTexture(void)
recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
#else
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
}

View File

@ -306,9 +306,10 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
Font font = { 0 };
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);
#if defined(SUPPORT_FILEFORMAT_TTF)
if (font.chars != NULL)
{
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);
}
else font = GetFontDefault();
#else
font = GetFontDefault();
#endif
return font;
}
@ -426,6 +430,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
spriteFont.chars[i].offsetX = 0;
spriteFont.chars[i].offsetY = 0;
spriteFont.chars[i].advanceX = 0;
spriteFont.chars[i].data = NULL;
}
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;
#if defined(SUPPORT_FILEFORMAT_TTF)
// Load font data (including pixel data) from TTF file
// NOTE: Loaded information should be enough to generate font image atlas,
// 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);
// 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
// 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);
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)
{
@ -537,18 +544,22 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
if (genFontChars) free(fontChars);
}
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;
}
// Generate image font atlas using chars info
// 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 atlas = { 0 };
// 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
// 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;
}
#endif
// Unload Font from GPU memory (VRAM)
void UnloadFont(Font font)
@ -674,6 +686,11 @@ void UnloadFont(Font font)
// NOTE: Make sure spriteFont is not default font (fallback)
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);
free(font.chars);
@ -780,14 +797,14 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
}
// Draw text using font inside rectangle limits
void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
{
DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
}
// Draw text using font inside rectangle limits with support for text selection
void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint,
int selectStart, int selectLength, Color selectText, Color selectBack)
void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint,
int selectStart, int selectLength, Color selectText, Color selectBack)
{
int length = strlen(text);
int textOffsetX = 0; // Offset between characters
@ -803,12 +820,12 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
int state = wordWrap? MEASURE_STATE : DRAW_STATE;
int startLine = -1; // Index where to begin drawing (where a line begins)
int endLine = -1; // Index where to stop drawing (where a line ends)
for (int i = 0; i < length; i++)
{
int glyphWidth = 0;
letter = (unsigned char)text[i];
if (letter != '\n')
{
if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK!
@ -826,41 +843,41 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
i++;
}
else index = GetGlyphIndex(font, (unsigned char)text[i]);
glyphWidth = (font.chars[index].advanceX == 0)?
glyphWidth = (font.chars[index].advanceX == 0)?
(int)(font.chars[index].rec.width*scaleFactor + spacing):
(int)(font.chars[index].advanceX*scaleFactor + spacing);
}
// NOTE: When wordWrap is ON we first measure how much of the text we can draw
// before going outside of the `rec` container. We store this info inside
// before going outside of the `rec` container. We store this info inside
// `startLine` and `endLine` then we change states, draw the text between those two
// variables then change states again and again recursively until the end of the text
// (or until we get outside of the container).
// When wordWrap is OFF we don't need the measure state so we go to the drawing
// state immediately and begin drawing on the next line before we can get outside
// When wordWrap is OFF we don't need the measure state so we go to the drawing
// state immediately and begin drawing on the next line before we can get outside
// the container.
if (state == MEASURE_STATE)
if (state == MEASURE_STATE)
{
if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i;
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 ((startLine + 1) == endLine) endLine = i - 1;
state = !state;
}
else if ((i + 1) == length)
}
else if ((i + 1) == length)
{
endLine = i;
state = !state;
}
else if (letter == '\n')
else if (letter == '\n')
{
state = !state;
}
if (state == DRAW_STATE)
{
textOffsetX = 0;
@ -868,8 +885,8 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
glyphWidth = 0;
}
}
else
}
else
{
if (letter == '\n')
{
@ -878,17 +895,17 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
textOffsetX = 0;
}
}
else
}
else
{
if (!wordWrap && ((textOffsetX + glyphWidth + 1) >= rec.width))
{
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
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
bool isGlyphSelected = false;
if ((selectStart >= 0) && (i >= selectStart) && (i < (selectStart + selectLength)))
@ -897,7 +914,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
DrawRectangleRec(strec, selectBack);
isGlyphSelected = true;
}
//draw glyph
if ((letter != ' ') && (letter != '\t'))
{
@ -905,12 +922,12 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
(Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
font.chars[index].rec.width*scaleFactor,
font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f,
(!isGlyphSelected) ? tint : selectText);
font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f,
(!isGlyphSelected)? tint : selectText);
}
}
if (wordWrap && (i == endLine))
if (wordWrap && (i == endLine))
{
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
textOffsetX = 0;
@ -920,7 +937,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
state = !state;
}
}
textOffsetX += glyphWidth;
}
}
@ -958,11 +975,11 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
unsigned char letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < len; i++)
{
lenCounter++;
if (text[i] != '\n')
{
if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification
@ -1095,7 +1112,7 @@ const char *TextSubtext(const char *text, int position, int length)
const char *TextReplace(char *text, const char *replace, const char *by)
{
char *result;
char *insertPoint; // Next insert point
char *temp; // Temp pointer
int replaceLen; // Replace string length of (the string to remove)
@ -1153,7 +1170,7 @@ const char *TextInsert(const char *text, const char *insert, int position)
for (int i = 0; i < position; i++) result[i] = text[i];
for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
result[textLen + insertLen] = '\0'; // Make sure text string is valid!
return result;
@ -1164,7 +1181,7 @@ const char *TextInsert(const char *text, const char *insert, int position)
const char *TextJoin(const char **textList, int count, const char *delimiter)
{
// TODO: Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(text, 0, MAX_TEXT_BUFFER_LENGTH);
@ -1187,9 +1204,9 @@ const char **TextSplit(const char *text, char delimiter, int *count)
// all used memory is static... it has some limitations:
// 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT
// 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
#define MAX_SUBSTRINGS_COUNT 64
static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL };
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@ -1198,7 +1215,7 @@ const char **TextSplit(const char *text, char delimiter, int *count)
int counter = 1;
// Count how many substrings we have on text and point to every one
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
{
buffer[i] = text[i];
if (buffer[i] == '\0') break;
@ -1207,7 +1224,7 @@ const char **TextSplit(const char *text, char delimiter, int *count)
buffer[i] = '\0'; // Set an end of string at this point
result[counter] = buffer + i + 1;
counter++;
if (counter == MAX_SUBSTRINGS_COUNT) break;
}
}
@ -1229,11 +1246,11 @@ void TextAppend(char *text, const char *append, int *position)
int TextFindIndex(const char *text, const char *find)
{
int position = -1;
char *ptr = strstr(text, find);
if (ptr != NULL) position = ptr - text;
return position;
}
@ -1304,10 +1321,10 @@ int TextToInteger(const char *text)
{
if ((text[i] > 47) && (text[i] < 58)) result += ((int)text[i] - 48)*units;
else { result = -1; break; }
units *= 10;
}
return result;
}
@ -1376,10 +1393,10 @@ static Font LoadBMFont(const char *fileName)
char *lastSlash = NULL;
lastSlash = strrchr(fileName, '/');
if (lastSlash == NULL)
{
lastSlash = strrchr(fileName, '\\');
}
if (lastSlash == NULL)
{
lastSlash = strrchr(fileName, '\\');
}
// NOTE: We need some extra space to avoid memory corruption on next allocations!
texPath = malloc(strlen(fileName) - strlen(lastSlash) + strlen(texFileName) + 4);
@ -1428,6 +1445,7 @@ static Font LoadBMFont(const char *fileName)
font.chars[i].offsetX = charOffsetX;
font.chars[i].offsetY = charOffsetY;
font.chars[i].advanceX = charAdvanceX;
font.chars[i].data = NULL;
}
fclose(fntFile);

View File

@ -182,7 +182,11 @@ Image LoadImage(const char *fileName)
{
Image image = { 0 };
#if defined(SUPPORT_FILEFORMAT_PNG)
if ((IsFileExtension(fileName, ".png"))
#else
if ((false)
#endif
#if defined(SUPPORT_FILEFORMAT_BMP)
|| (IsFileExtension(fileName, ".bmp"))
#endif
@ -398,90 +402,6 @@ Texture2D LoadTextureFromImage(Image image)
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)
// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
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].y = (float)((pixel & 0b0000011111000000) >> 6)*(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;
case UNCOMPRESSED_R5G6B5:
@ -825,14 +745,27 @@ void ExportImage(Image image, const char *fileName)
{
int success = 0;
#if defined(SUPPORT_IMAGE_EXPORT)
// NOTE: Getting Color array as RGBA unsigned char values
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);
#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);
#endif
#if defined(SUPPORT_FILEFORMAT_TGA)
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
#endif
#if defined(SUPPORT_FILEFORMAT_KTX)
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
#endif
else if (IsFileExtension(fileName, ".raw"))
{
// Export raw pixel data (without header)
@ -842,10 +775,11 @@ void ExportImage(Image image, const char *fileName)
fclose(rawFile);
}
free(imgData);
#endif
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
else TraceLog(LOG_WARNING, "Image could not be exported.");
free(imgData);
}
// 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, "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]);
fclose(txtFile);
@ -1050,7 +984,7 @@ void ImageFormat(Image *image, int newFormat)
r = (unsigned char)(round(pixels[i].x*31.0f));
g = (unsigned char)(round(pixels[i].y*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;
}
@ -1133,7 +1067,9 @@ void ImageFormat(Image *image, int newFormat)
if (image->mipmaps > 1)
{
image->mipmaps = 1;
#if defined(SUPPORT_IMAGE_MANIPULATION)
if (image->data != NULL) ImageMipmaps(image);
#endif
}
}
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);
}
// 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
void ImageAlphaPremultiply(Image *image)
{
@ -1259,6 +1163,90 @@ void ImageAlphaPremultiply(Image *image)
#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
// NOTE: Security checks are performed in case rectangle goes out of bounds
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
// NOTE: Uses stb default scaling filters (both bicubic):
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
@ -1611,7 +1631,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
if (palCount >= maxPaletteSize)
{
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
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: plain color
Image GenImageColor(int width, int height, Color color)
{
@ -2161,6 +2180,7 @@ Image GenImageColor(int width, int height, Color color)
return image;
}
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: vertical gradient
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)
{
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 centerY = (float)height/2.0f;
@ -2509,7 +2529,7 @@ void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangl
{
Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height };
Vector2 origin = { 0.0f, 0.0f };
DrawTexturePro(texture, source, quad, origin, 0.0f, tint);
}
@ -3173,25 +3193,27 @@ static int SaveKTX(Image image, const char *fileName)
{
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] = { 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
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.glType = 0; // Obtained from image.format
ktxHeader.glType = 0; // Obtained from image.format
ktxHeader.glTypeSize = 1;
ktxHeader.glFormat = 0; // Obtained from image.format
ktxHeader.glInternalFormat = 0; // Obtained from image.format
ktxHeader.glFormat = 0; // Obtained from image.format
ktxHeader.glInternalFormat = 0; // Obtained from image.format
ktxHeader.glBaseInternalFormat = 0;
ktxHeader.width = image.width;
ktxHeader.height = image.height;
ktxHeader.depth = 0;
ktxHeader.elements = 0;
ktxHeader.faces = 1;
ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
ktxHeader.keyValueDataSize = 0; // No extra data after the header
ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
ktxHeader.keyValueDataSize = 0; // No extra data after the header
rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only