Merge branch 'master' of https://github.com/raysan5/raylib into raysan

This commit is contained in:
jseb 2019-10-28 20:21:58 +01:00
commit 2469f46950
24 changed files with 803 additions and 578 deletions

View File

@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to
- [cray](https://github.com/tapgg/cray) : raylib **Crystal** binding - [cray](https://github.com/tapgg/cray) : raylib **Crystal** binding
- [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper
- [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding
- [raylib-pas](https://github.com/tazdij/raylib-pas) - raylib **Pascal** binding (including rlgl & raymath)
- [Raylib-cs](https://github.com/ChrisDill/Raylib-cs) : raylib **C#** binding - [Raylib-cs](https://github.com/ChrisDill/Raylib-cs) : raylib **C#** binding
- [RaylibSharp](https://github.com/TheLumaio/RaylibSharp) : raylib **C#** binding - [RaylibSharp](https://github.com/TheLumaio/RaylibSharp) : raylib **C#** binding
- [raylib-ruby-ffi](https://github.com/D3nX/raylib-ruby-ffi) : raylib **Ruby** binding - [raylib-ruby-ffi](https://github.com/D3nX/raylib-ruby-ffi) : raylib **Ruby** binding

View File

@ -438,7 +438,7 @@ EXAMPLES = \
shaders/shaders_eratosthenes \ shaders/shaders_eratosthenes \
shaders/shaders_basic_lighting \ shaders/shaders_basic_lighting \
shaders/shaders_fog \ shaders/shaders_fog \
shaders/shaders_simple \ shaders/shaders_simple_mask \
audio/audio_module_playing \ audio/audio_module_playing \
audio/audio_music_stream \ audio/audio_music_stream \
audio/audio_raw_stream \ audio/audio_raw_stream \

View File

@ -8,8 +8,6 @@ varying vec4 fragColor;
uniform vec3 viewEye; uniform vec3 viewEye;
uniform vec3 viewCenter; uniform vec3 viewCenter;
uniform vec3 viewUp;
uniform float deltaTime;
uniform float runTime; uniform float runTime;
uniform vec2 resolution; uniform vec2 resolution;
@ -428,4 +426,4 @@ void main()
#endif #endif
gl_FragColor = vec4( tot, 1.0 ); gl_FragColor = vec4( tot, 1.0 );
} }

View File

@ -9,8 +9,6 @@ out vec4 finalColor;
uniform vec3 viewEye; uniform vec3 viewEye;
uniform vec3 viewCenter; uniform vec3 viewCenter;
uniform vec3 viewUp;
uniform float deltaTime;
uniform float runTime; uniform float runTime;
uniform vec2 resolution; uniform vec2 resolution;
@ -429,4 +427,4 @@ void main()
#endif #endif
finalColor = vec4( tot, 1.0 ); finalColor = vec4( tot, 1.0 );
} }

View File

@ -28,9 +28,10 @@ int main(void)
{ {
// Initialization // Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
const int screenWidth = 800; int screenWidth = 800;
const int screenHeight = 450; int screenHeight = 450;
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes"); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes");
Camera camera = { 0 }; Camera camera = { 0 };
@ -48,12 +49,10 @@ int main(void)
// Get shader locations for required uniforms // Get shader locations for required uniforms
int viewEyeLoc = GetShaderLocation(shader, "viewEye"); int viewEyeLoc = GetShaderLocation(shader, "viewEye");
int viewCenterLoc = GetShaderLocation(shader, "viewCenter"); int viewCenterLoc = GetShaderLocation(shader, "viewCenter");
int viewUpLoc = GetShaderLocation(shader, "viewUp");
int deltaTimeLoc = GetShaderLocation(shader, "deltaTime");
int runTimeLoc = GetShaderLocation(shader, "runTime"); int runTimeLoc = GetShaderLocation(shader, "runTime");
int resolutionLoc = GetShaderLocation(shader, "resolution"); int resolutionLoc = GetShaderLocation(shader, "resolution");
float resolution[2] = { screenWidth, screenHeight }; float resolution[2] = { (float)screenWidth, (float)screenHeight };
SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2); SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2);
float runTime = 0.0f; float runTime = 0.0f;
@ -64,13 +63,22 @@ int main(void)
// Main game loop // Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key while (!WindowShouldClose()) // Detect window close button or ESC key
{ {
// Check if screen is resized
//----------------------------------------------------------------------------------
if(IsWindowResized())
{
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
float resolution[2] = { (float)screenWidth, (float)screenHeight };
SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2);
}
// Update // Update
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
UpdateCamera(&camera); // Update camera UpdateCamera(&camera); // Update camera
float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z }; float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z };
float cameraUp[3] = { camera.up.x, camera.up.y, camera.up.z };
float deltaTime = GetFrameTime(); float deltaTime = GetFrameTime();
runTime += deltaTime; runTime += deltaTime;
@ -78,8 +86,6 @@ int main(void)
// Set shader required uniform values // Set shader required uniform values
SetShaderValue(shader, viewEyeLoc, cameraPos, UNIFORM_VEC3); SetShaderValue(shader, viewEyeLoc, cameraPos, UNIFORM_VEC3);
SetShaderValue(shader, viewCenterLoc, cameraTarget, UNIFORM_VEC3); SetShaderValue(shader, viewCenterLoc, cameraTarget, UNIFORM_VEC3);
SetShaderValue(shader, viewUpLoc, cameraUp, UNIFORM_VEC3);
SetShaderValue(shader, deltaTimeLoc, &deltaTime, UNIFORM_FLOAT);
SetShaderValue(shader, runTimeLoc, &runTime, UNIFORM_FLOAT); SetShaderValue(shader, runTimeLoc, &runTime, UNIFORM_FLOAT);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -95,7 +101,7 @@ int main(void)
DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
EndShaderMode(); EndShaderMode();
DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, GRAY); DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, BLACK);
EndDrawing(); EndDrawing();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -109,4 +115,4 @@ int main(void)
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
return 0; return 0;
} }

View File

@ -8,7 +8,7 @@
* *
* #define CAMERA_IMPLEMENTATION * #define CAMERA_IMPLEMENTATION
* Generates the implementation of the library into the included file. * Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers * If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation. * or source files without problems. But only ONE file should hold the implementation.
* *
* #define CAMERA_STANDALONE * #define CAMERA_STANDALONE
@ -77,7 +77,7 @@
} Camera3D; } Camera3D;
typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
// Camera system modes // Camera system modes
typedef enum { typedef enum {
CAMERA_CUSTOM = 0, CAMERA_CUSTOM = 0,
@ -113,8 +113,8 @@ void UpdateCamera(Camera *camera); // Update camera pos
void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera) void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera)
void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera) void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera)
void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera)
void SetCameraMoveControls(int frontKey, int backKey, void SetCameraMoveControls(int frontKey, int backKey,
int rightKey, int leftKey, int rightKey, int leftKey,
int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras) int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras)
#endif #endif
@ -188,21 +188,21 @@ void SetCameraMoveControls(int frontKey, int backKey,
// Types and Structures Definition // Types and Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Camera move modes (first person and third person cameras) // Camera move modes (first person and third person cameras)
typedef enum { typedef enum {
MOVE_FRONT = 0, MOVE_FRONT = 0,
MOVE_BACK, MOVE_BACK,
MOVE_RIGHT, MOVE_RIGHT,
MOVE_LEFT, MOVE_LEFT,
MOVE_UP, MOVE_UP,
MOVE_DOWN MOVE_DOWN
} CameraMove; } CameraMove;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static Vector2 cameraAngle = { 0.0f, 0.0f }; // Camera angle in plane XZ static Vector2 cameraAngle = { 0.0f, 0.0f }; // Camera angle in plane XZ
static float cameraTargetDistance = 0.0f; // Camera distance from position to target static float cameraTargetDistance = 0.0f; // Camera distance from position to target
static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters) static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters)
static int cameraMoveControl[6] = { 'W', 'S', 'D', 'A', 'E', 'Q' }; static int cameraMoveControl[6] = { 'W', 'S', 'D', 'A', 'E', 'Q' };
static int cameraPanControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON static int cameraPanControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON
@ -236,21 +236,21 @@ void SetCameraMode(Camera camera, int mode)
{ {
Vector3 v1 = camera.position; Vector3 v1 = camera.position;
Vector3 v2 = camera.target; Vector3 v2 = camera.target;
float dx = v2.x - v1.x; float dx = v2.x - v1.x;
float dy = v2.y - v1.y; float dy = v2.y - v1.y;
float dz = v2.z - v1.z; float dz = v2.z - v1.z;
cameraTargetDistance = sqrtf(dx*dx + dy*dy + dz*dz); cameraTargetDistance = sqrtf(dx*dx + dy*dy + dz*dz);
Vector2 distance = { 0.0f, 0.0f }; Vector2 distance = { 0.0f, 0.0f };
distance.x = sqrtf(dx*dx + dz*dz); distance.x = sqrtf(dx*dx + dz*dz);
distance.y = sqrtf(dx*dx + dy*dy); distance.y = sqrtf(dx*dx + dy*dy);
// Camera angle calculation // Camera angle calculation
cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW)
playerEyesPosition = camera.position.y; playerEyesPosition = camera.position.y;
// Lock cursor for first person and third person cameras // Lock cursor for first person and third person cameras
@ -272,24 +272,24 @@ void UpdateCamera(Camera *camera)
static Vector2 previousMousePosition = { 0.0f, 0.0f }; static Vector2 previousMousePosition = { 0.0f, 0.0f };
// TODO: Compute cameraTargetDistance and cameraAngle here // TODO: Compute cameraTargetDistance and cameraAngle here
// Mouse movement detection // Mouse movement detection
Vector2 mousePositionDelta = { 0.0f, 0.0f }; Vector2 mousePositionDelta = { 0.0f, 0.0f };
Vector2 mousePosition = GetMousePosition(); Vector2 mousePosition = GetMousePosition();
int mouseWheelMove = GetMouseWheelMove(); int mouseWheelMove = GetMouseWheelMove();
// Keys input detection // Keys input detection
bool panKey = IsMouseButtonDown(cameraPanControlKey); bool panKey = IsMouseButtonDown(cameraPanControlKey);
bool altKey = IsKeyDown(cameraAltControlKey); bool altKey = IsKeyDown(cameraAltControlKey);
bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey); bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey);
bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]), bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]),
IsKeyDown(cameraMoveControl[MOVE_BACK]), IsKeyDown(cameraMoveControl[MOVE_BACK]),
IsKeyDown(cameraMoveControl[MOVE_RIGHT]), IsKeyDown(cameraMoveControl[MOVE_RIGHT]),
IsKeyDown(cameraMoveControl[MOVE_LEFT]), IsKeyDown(cameraMoveControl[MOVE_LEFT]),
IsKeyDown(cameraMoveControl[MOVE_UP]), IsKeyDown(cameraMoveControl[MOVE_UP]),
IsKeyDown(cameraMoveControl[MOVE_DOWN]) }; IsKeyDown(cameraMoveControl[MOVE_DOWN]) };
// TODO: Consider touch inputs for camera // TODO: Consider touch inputs for camera
if (cameraMode != CAMERA_CUSTOM) if (cameraMode != CAMERA_CUSTOM)
@ -384,7 +384,7 @@ void UpdateCamera(Camera *camera)
camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
} }
} }
// Update camera position with changes // Update camera position with changes
camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x;
camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y;
@ -395,15 +395,15 @@ void UpdateCamera(Camera *camera)
{ {
cameraAngle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle cameraAngle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle
cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom
// Camera distance clamp // Camera distance clamp
if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
// Update camera position with changes // Update camera position with changes
camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x;
camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y;
camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z; camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z;
} break; } break;
case CAMERA_FIRST_PERSON: case CAMERA_FIRST_PERSON:
{ {
@ -411,11 +411,11 @@ void UpdateCamera(Camera *camera)
sinf(cameraAngle.x)*direction[MOVE_FRONT] - sinf(cameraAngle.x)*direction[MOVE_FRONT] -
cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_LEFT] +
cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] -
sinf(cameraAngle.y)*direction[MOVE_BACK] + sinf(cameraAngle.y)*direction[MOVE_BACK] +
1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY;
camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] -
cosf(cameraAngle.x)*direction[MOVE_FRONT] + cosf(cameraAngle.x)*direction[MOVE_FRONT] +
sinf(cameraAngle.x)*direction[MOVE_LEFT] - sinf(cameraAngle.x)*direction[MOVE_LEFT] -
@ -424,11 +424,11 @@ void UpdateCamera(Camera *camera)
bool isMoving = false; // Required for swinging bool isMoving = false; // Required for swinging
for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; } for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; }
// Camera orientation calculation // Camera orientation calculation
cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
// Angle clamp // Angle clamp
if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD;
else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD;
@ -437,7 +437,7 @@ void UpdateCamera(Camera *camera)
camera->target.x = camera->position.x - sinf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.x = camera->position.x - sinf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
camera->target.y = camera->position.y + sinf(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.y = camera->position.y + sinf(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
camera->target.z = camera->position.z - cosf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.z = camera->position.z - cosf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
if (isMoving) swingCounter++; if (isMoving) swingCounter++;
// Camera position update // Camera position update
@ -446,8 +446,8 @@ void UpdateCamera(Camera *camera)
camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
} break; } break;
case CAMERA_THIRD_PERSON: case CAMERA_THIRD_PERSON:
{ {
@ -455,11 +455,11 @@ void UpdateCamera(Camera *camera)
sinf(cameraAngle.x)*direction[MOVE_FRONT] - sinf(cameraAngle.x)*direction[MOVE_FRONT] -
cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_LEFT] +
cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] -
sinf(cameraAngle.y)*direction[MOVE_BACK] + sinf(cameraAngle.y)*direction[MOVE_BACK] +
1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY;
camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] -
cosf(cameraAngle.x)*direction[MOVE_FRONT] + cosf(cameraAngle.x)*direction[MOVE_FRONT] +
sinf(cameraAngle.x)*direction[MOVE_LEFT] - sinf(cameraAngle.x)*direction[MOVE_LEFT] -
@ -468,7 +468,7 @@ void UpdateCamera(Camera *camera)
// Camera orientation calculation // Camera orientation calculation
cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
// Angle clamp // Angle clamp
if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD;
else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD;
@ -487,7 +487,7 @@ void UpdateCamera(Camera *camera)
} break; } break;
default: break; default: break;
} }
} }
// Set camera pan key to combine with mouse movement (free camera) // Set camera pan key to combine with mouse movement (free camera)

View File

@ -579,7 +579,7 @@ static void InitTerminal(void)
} }
else else
{ {
ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE);
} }
@ -590,7 +590,7 @@ static void InitTerminal(void)
static void RestoreTerminal(void) static void RestoreTerminal(void)
{ {
TraceLog(LOG_INFO, "Restore Terminal ..."); TraceLog(LOG_INFO, "Restore Terminal ...");
// Reset to default keyboard settings // Reset to default keyboard settings
tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings); tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings);
@ -774,7 +774,7 @@ void CloseWindow(void)
pthread_join(eventWorkers[i].threadId, NULL); pthread_join(eventWorkers[i].threadId, NULL);
} }
} }
if (gamepadThreadId) pthread_join(gamepadThreadId, NULL); if (gamepadThreadId) pthread_join(gamepadThreadId, NULL);
#endif #endif
@ -1207,7 +1207,7 @@ void EndDrawing(void)
// we draw a small rectangle for user reference // we draw a small rectangle for user reference
DrawRectangle(mousePosition.x, mousePosition.y, 3, 3, MAROON); DrawRectangle(mousePosition.x, mousePosition.y, 3, 3, MAROON);
#endif #endif
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
#if defined(SUPPORT_GIF_RECORDING) #if defined(SUPPORT_GIF_RECORDING)
@ -1270,10 +1270,10 @@ void BeginMode2D(Camera2D camera)
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
rlLoadIdentity(); // Reset current matrix (MODELVIEW) rlLoadIdentity(); // Reset current matrix (MODELVIEW)
// Apply screen scaling if required // Apply screen scaling if required
rlMultMatrixf(MatrixToFloat(screenScaling)); rlMultMatrixf(MatrixToFloat(screenScaling));
// Apply 2d camera transformation to modelview // Apply 2d camera transformation to modelview
rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera))); rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
} }
@ -1465,7 +1465,7 @@ Matrix GetCameraMatrix(Camera camera)
} }
// Returns camera 2d transform matrix // Returns camera 2d transform matrix
Matrix GetCameraMatrix2D(Camera2D camera) Matrix GetCameraMatrix2D(Camera2D camera)
{ {
Matrix matTransform = { 0 }; Matrix matTransform = { 0 };
// The camera in world-space is set by // The camera in world-space is set by
@ -1486,9 +1486,9 @@ Matrix GetCameraMatrix2D(Camera2D camera)
Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD); Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f); Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f); Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
return matTransform; return matTransform;
} }
@ -1535,20 +1535,20 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera)
} }
// Returns the screen space position for a 2d camera world space position // Returns the screen space position for a 2d camera world space position
Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera) Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
{ {
Matrix matCamera = GetCameraMatrix2D(camera); Matrix matCamera = GetCameraMatrix2D(camera);
Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera); Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
return (Vector2){ transform.x, transform.y }; return (Vector2){ transform.x, transform.y };
} }
// Returns the world space position for a 2d camera screen space position // Returns the world space position for a 2d camera screen space position
Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
{ {
Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera)); Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera); Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
return (Vector2){ transform.x, transform.y }; return (Vector2){ transform.x, transform.y };
} }
@ -1796,29 +1796,21 @@ bool FileExists(const char *fileName)
bool IsFileExtension(const char *fileName, const char *ext) bool IsFileExtension(const char *fileName, const char *ext)
{ {
bool result = false; bool result = false;
const char *fileExt; const char *fileExt = GetExtension(fileName);
if ((fileExt = strrchr(fileName, '.')) != NULL) if (fileExt != NULL)
{ {
#if defined(_WIN32) int extCount = 0;
result = true; const char **checkExts = TextSplit(ext, ';', &extCount);
int extLen = strlen(ext);
if (strlen(fileExt) == extLen) for (int i = 0; i < extCount; i++)
{ {
for (int i = 0; i < extLen; i++) if (strcmp(fileExt, checkExts[i] + 1) == 0)
{ {
if (tolower(fileExt[i]) != tolower(ext[i])) result = true;
{ break;
result = false;
break;
}
} }
} }
else result = false;
#else
if (strcmp(fileExt, ext) == 0) result = true;
#endif
} }
return result; return result;
@ -1916,9 +1908,9 @@ const char *GetPrevDirectoryPath(const char *dirPath)
static char prevDirPath[MAX_FILEPATH_LENGTH]; static char prevDirPath[MAX_FILEPATH_LENGTH];
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH); memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
int pathLen = strlen(dirPath); int pathLen = strlen(dirPath);
if (pathLen <= 3) strcpy(prevDirPath, dirPath); if (pathLen <= 3) strcpy(prevDirPath, dirPath);
for (int i = (pathLen - 1); (i > 0) && (pathLen > 3); i--) for (int i = (pathLen - 1); (i > 0) && (pathLen > 3); i--)
{ {
if ((dirPath[i] == '\\') || (dirPath[i] == '/')) if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
@ -1928,7 +1920,7 @@ const char *GetPrevDirectoryPath(const char *dirPath)
break; break;
} }
} }
return prevDirPath; return prevDirPath;
} }
@ -1986,11 +1978,12 @@ void ClearDirectoryFiles(void)
{ {
if (dirFilesCount > 0) if (dirFilesCount > 0)
{ {
for (int i = 0; i < dirFilesCount; i++) RL_FREE(dirFilesPath[i]); for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesPath[i]);
RL_FREE(dirFilesPath); RL_FREE(dirFilesPath);
dirFilesCount = 0;
} }
dirFilesCount = 0;
} }
// Change working directory, returns true if success // Change working directory, returns true if success
@ -2045,9 +2038,9 @@ long GetFileModTime(const char *fileName)
unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength) unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength)
{ {
#define COMPRESSION_QUALITY_DEFLATE 8 #define COMPRESSION_QUALITY_DEFLATE 8
unsigned char *compData = NULL; unsigned char *compData = NULL;
#if defined(SUPPORT_COMPRESSION_API) #if defined(SUPPORT_COMPRESSION_API)
compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE); compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE);
#endif #endif
@ -2059,7 +2052,7 @@ unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLe
unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength) unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength)
{ {
char *data = NULL; char *data = NULL;
#if defined(SUPPORT_COMPRESSION_API) #if defined(SUPPORT_COMPRESSION_API)
data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength); data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength);
#endif #endif
@ -2664,9 +2657,9 @@ static bool InitGraphicsDevice(int width, int height)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
#else #else
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
#endif #endif
} }
@ -4931,7 +4924,7 @@ static void *EventThread(void *arg)
// TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented)
} }
*/ */
currentKeyState[keycode] = event.value; currentKeyState[keycode] = event.value;
if (event.value == 1) lastKeyPressed = keycode; // Register last key pressed if (event.value == 1) lastKeyPressed = keycode; // Register last key pressed
@ -4945,7 +4938,7 @@ static void *EventThread(void *arg)
#endif #endif
if (currentKeyState[exitKey] == 1) windowShouldClose = true; if (currentKeyState[exitKey] == 1) windowShouldClose = true;
TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode);
} }
} }

View File

@ -1,4 +1,4 @@
/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb /* stb_image - v2.23 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk no warranty implied; use at your own risk
Do this: Do this:
@ -48,6 +48,7 @@ LICENSE
RECENT REVISION HISTORY: RECENT REVISION HISTORY:
2.23 (2019-08-11) fix clang static analysis warning
2.22 (2019-03-04) gif fixes, fix warnings 2.22 (2019-03-04) gif fixes, fix warnings
2.21 (2019-02-25) fix typo in comment 2.21 (2019-02-25) fix typo in comment
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
@ -5079,7 +5080,7 @@ static int stbi__high_bit(unsigned int z)
if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00100) { n += 8; z >>= 8; }
if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00010) { n += 4; z >>= 4; }
if (z >= 0x00004) { n += 2; z >>= 2; } if (z >= 0x00004) { n += 2; z >>= 2; }
if (z >= 0x00002) { n += 1; z >>= 1; } if (z >= 0x00002) { n += 1;/* >>= 1;*/ }
return n; return n;
} }
@ -5237,7 +5238,10 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - 14 - info.hsz) >> 2; psize = (info.offset - 14 - info.hsz) >> 2;
} }
s->img_n = ma ? 4 : 3; if (info.bpp == 24 && ma == 0xff000000)
s->img_n = 3;
else
s->img_n = ma ? 4 : 3;
if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
target = req_comp; target = req_comp;
else else
@ -5547,6 +5551,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
int RLE_repeating = 0; int RLE_repeating = 0;
int read_next_pixel = 1; int read_next_pixel = 1;
STBI_NOTUSED(ri); STBI_NOTUSED(ri);
STBI_NOTUSED(tga_x_origin); // @TODO
STBI_NOTUSED(tga_y_origin); // @TODO
// do a tiny bit of precessing // do a tiny bit of precessing
if ( tga_image_type >= 8 ) if ( tga_image_type >= 8 )
@ -5710,6 +5716,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
// Microsoft's C compilers happy... [8^( // Microsoft's C compilers happy... [8^(
tga_palette_start = tga_palette_len = tga_palette_bits = tga_palette_start = tga_palette_len = tga_palette_bits =
tga_x_origin = tga_y_origin = 0; tga_x_origin = tga_y_origin = 0;
STBI_NOTUSED(tga_palette_start);
// OK, done // OK, done
return tga_data; return tga_data;
} }
@ -6936,7 +6943,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
return 0; return 0;
if (x) *x = s->img_x; if (x) *x = s->img_x;
if (y) *y = s->img_y; if (y) *y = s->img_y;
if (comp) *comp = info.ma ? 4 : 3; if (comp) {
if (info.bpp == 24 && info.ma == 0xff000000)
*comp = 3;
else
*comp = info.ma ? 4 : 3;
}
return 1; return 1;
} }
#endif #endif

View File

@ -1,4 +1,4 @@
/* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h /* stb_image_write - v1.13 - public domain - http://nothings.org/stb
writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
no warranty implied; use at your own risk no warranty implied; use at your own risk
@ -868,7 +868,7 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
unsigned int bitbuf=0; unsigned int bitbuf=0;
int i,j, bitcount=0; int i,j, bitcount=0;
unsigned char *out = NULL; unsigned char *out = NULL;
unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));
if (hash_table == NULL) if (hash_table == NULL)
return NULL; return NULL;
if (quality < 5) quality = 5; if (quality < 5) quality = 5;
@ -1530,6 +1530,8 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const
#endif // STB_IMAGE_WRITE_IMPLEMENTATION #endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history /* Revision history
1.11 (2019-08-11)
1.10 (2019-02-07) 1.10 (2019-02-07)
support utf8 filenames in Windows; fix warnings and platform ifdefs support utf8 filenames in Windows; fix warnings and platform ifdefs
1.09 (2018-02-11) 1.09 (2018-02-11)

View File

@ -81,6 +81,7 @@ extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap
extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves);
extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@ -321,6 +322,66 @@ float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity,
return sum; return sum;
} }
float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
{
float u,v,w;
float n000,n001,n010,n011,n100,n101,n110,n111;
float n00,n01,n10,n11;
float n0,n1;
int px = stb__perlin_fastfloor(x);
int py = stb__perlin_fastfloor(y);
int pz = stb__perlin_fastfloor(z);
int x_wrap2 = (x_wrap ? x_wrap : 256);
int y_wrap2 = (y_wrap ? y_wrap : 256);
int z_wrap2 = (z_wrap ? z_wrap : 256);
int x0 = px % x_wrap2, x1;
int y0 = py % y_wrap2, y1;
int z0 = pz % z_wrap2, z1;
int r0,r1, r00,r01,r10,r11;
if (x0 < 0) x0 += x_wrap2;
if (y0 < 0) y0 += y_wrap2;
if (z0 < 0) z0 += z_wrap2;
x1 = (x0+1) % x_wrap2;
y1 = (y0+1) % y_wrap2;
z1 = (z0+1) % z_wrap2;
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
x -= px; u = stb__perlin_ease(x);
y -= py; v = stb__perlin_ease(y);
z -= pz; w = stb__perlin_ease(z);
r0 = stb__perlin_randtab[x0];
r0 = stb__perlin_randtab[r0+seed];
r1 = stb__perlin_randtab[x1];
r1 = stb__perlin_randtab[r1+seed];
r00 = stb__perlin_randtab[r0+y0];
r01 = stb__perlin_randtab[r0+y1];
r10 = stb__perlin_randtab[r1+y0];
r11 = stb__perlin_randtab[r1+y1];
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
n00 = stb__perlin_lerp(n000,n001,w);
n01 = stb__perlin_lerp(n010,n011,w);
n10 = stb__perlin_lerp(n100,n101,w);
n11 = stb__perlin_lerp(n110,n111,w);
n0 = stb__perlin_lerp(n00,n01,v);
n1 = stb__perlin_lerp(n10,n11,v);
return stb__perlin_lerp(n0,n1,u);
}
#endif // STB_PERLIN_IMPLEMENTATION #endif // STB_PERLIN_IMPLEMENTATION
/* /*

View File

@ -1,5 +1,5 @@
// stb_truetype.h - v1.21 - public domain // stb_truetype.h - v1.22 - public domain
// authored from 2009-2016 by Sean Barrett / RAD Game Tools // authored from 2009-2019 by Sean Barrett / RAD Game Tools
// //
// This library processes TrueType files: // This library processes TrueType files:
// parse files // parse files
@ -46,9 +46,11 @@
// Rob Loach Cort Stratton // Rob Loach Cort Stratton
// Kenney Phillis Jr. github:oyvindjam // Kenney Phillis Jr. github:oyvindjam
// Brian Costabile github:vassvik // Brian Costabile github:vassvik
// Ken Voskuil (kaesve) Ryan Griege
// //
// VERSION HISTORY // VERSION HISTORY
// //
// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
// 1.21 (2019-02-25) fix warning // 1.21 (2019-02-25) fix warning
// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
@ -2540,8 +2542,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int
if (info->gpos) if (info->gpos)
xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
else if (info->kern)
if (info->kern)
xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
return xAdvance; return xAdvance;
@ -3968,6 +3969,7 @@ static float stbtt__oversample_shift(int oversample)
STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{ {
int i,j,k; int i,j,k;
int missing_glyph_added = 0;
k=0; k=0;
for (i=0; i < num_ranges; ++i) { for (i=0; i < num_ranges; ++i) {
@ -3979,7 +3981,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
int x0,y0,x1,y1; int x0,y0,x1,y1;
int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
int glyph = stbtt_FindGlyphIndex(info, codepoint); int glyph = stbtt_FindGlyphIndex(info, codepoint);
if (glyph == 0 && spc->skip_missing) { if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
rects[k].w = rects[k].h = 0; rects[k].w = rects[k].h = 0;
} else { } else {
stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
@ -3989,6 +3991,8 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
&x0,&y0,&x1,&y1); &x0,&y0,&x1,&y1);
rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
if (glyph == 0)
missing_glyph_added = 1;
} }
++k; ++k;
} }
@ -4023,7 +4027,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info
// rects array must be big enough to accommodate all characters in the given ranges // rects array must be big enough to accommodate all characters in the given ranges
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{ {
int i,j,k, return_value = 1; int i,j,k, missing_glyph = -1, return_value = 1;
// save current values // save current values
int old_h_over = spc->h_oversample; int old_h_over = spc->h_oversample;
@ -4088,6 +4092,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const
bc->yoff = (float) y0 * recip_v + sub_y; bc->yoff = (float) y0 * recip_v + sub_y;
bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
bc->yoff2 = (y0 + r->h) * recip_v + sub_y; bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
if (glyph == 0)
missing_glyph = j;
} else if (spc->skip_missing) {
return_value = 0;
} else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
} else { } else {
return_value = 0; // if any fail, report failure return_value = 0; // if any fail, report failure
} }
@ -4389,12 +4400,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
int w,h; int w,h;
unsigned char *data; unsigned char *data;
// if one scale is 0, use same scale for both if (scale == 0) return NULL;
if (scale_x == 0) scale_x = scale_y;
if (scale_y == 0) {
if (scale_x == 0) return NULL; // if both scales are 0, return NULL
scale_y = scale_x;
}
stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);

View File

@ -8,7 +8,7 @@
* *
* #define GESTURES_IMPLEMENTATION * #define GESTURES_IMPLEMENTATION
* Generates the implementation of the library into the included file. * Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers * If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation. * or source files without problems. But only ONE file should hold the implementation.
* *
* #define GESTURES_STANDALONE * #define GESTURES_STANDALONE
@ -216,8 +216,8 @@ static float pinchDistance = 0.0f; // PINCH displacement distance (
static int currentGesture = GESTURE_NONE; // Current detected gesture static int currentGesture = GESTURE_NONE; // Current detected gesture
// Enabled gestures flags, all gestures enabled by default // Enabled gestures flags, all gestures enabled by default
static unsigned int enabledGestures = 0b0000001111111111; static unsigned int enabledGestures = 0b0000001111111111;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
@ -251,13 +251,13 @@ void ProcessGestureEvent(GestureEvent event)
{ {
// Reset required variables // Reset required variables
pointCount = event.pointCount; // Required on UpdateGestures() pointCount = event.pointCount; // Required on UpdateGestures()
if (pointCount < 2) if (pointCount < 2)
{ {
if (event.touchAction == TOUCH_DOWN) if (event.touchAction == TOUCH_DOWN)
{ {
tapCounter++; // Tap counter tapCounter++; // Tap counter
// Detect GESTURE_DOUBLE_TAP // Detect GESTURE_DOUBLE_TAP
if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE)) if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE))
{ {
@ -269,15 +269,15 @@ void ProcessGestureEvent(GestureEvent event)
tapCounter = 1; tapCounter = 1;
currentGesture = GESTURE_TAP; currentGesture = GESTURE_TAP;
} }
touchDownPosition = event.position[0]; touchDownPosition = event.position[0];
touchDownDragPosition = event.position[0]; touchDownDragPosition = event.position[0];
touchUpPosition = touchDownPosition; touchUpPosition = touchDownPosition;
eventTime = GetCurrentTime(); eventTime = GetCurrentTime();
firstTouchId = event.pointerId[0]; firstTouchId = event.pointerId[0];
dragVector = (Vector2){ 0.0f, 0.0f }; dragVector = (Vector2){ 0.0f, 0.0f };
} }
else if (event.touchAction == TOUCH_UP) else if (event.touchAction == TOUCH_UP)
@ -287,15 +287,15 @@ void ProcessGestureEvent(GestureEvent event)
// NOTE: dragIntensity dependend on the resolution of the screen // NOTE: dragIntensity dependend on the resolution of the screen
dragDistance = Vector2Distance(touchDownPosition, touchUpPosition); dragDistance = Vector2Distance(touchDownPosition, touchUpPosition);
dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime)); dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime));
startMoving = false; startMoving = false;
// Detect GESTURE_SWIPE // Detect GESTURE_SWIPE
if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0])) if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0]))
{ {
// NOTE: Angle should be inverted in Y // NOTE: Angle should be inverted in Y
dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition); dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition);
if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right
else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up
else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left
@ -307,31 +307,31 @@ void ProcessGestureEvent(GestureEvent event)
dragDistance = 0.0f; dragDistance = 0.0f;
dragIntensity = 0.0f; dragIntensity = 0.0f;
dragAngle = 0.0f; dragAngle = 0.0f;
currentGesture = GESTURE_NONE; currentGesture = GESTURE_NONE;
} }
touchDownDragPosition = (Vector2){ 0.0f, 0.0f }; touchDownDragPosition = (Vector2){ 0.0f, 0.0f };
pointCount = 0; pointCount = 0;
} }
else if (event.touchAction == TOUCH_MOVE) else if (event.touchAction == TOUCH_MOVE)
{ {
if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime(); if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime();
if (!startMoving) if (!startMoving)
{ {
swipeTime = GetCurrentTime(); swipeTime = GetCurrentTime();
startMoving = true; startMoving = true;
} }
moveDownPosition = event.position[0]; moveDownPosition = event.position[0];
if (currentGesture == GESTURE_HOLD) if (currentGesture == GESTURE_HOLD)
{ {
if (resetHold) touchDownPosition = event.position[0]; if (resetHold) touchDownPosition = event.position[0];
resetHold = false; resetHold = false;
// Detect GESTURE_DRAG // Detect GESTURE_DRAG
if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG) if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG)
{ {
@ -339,7 +339,7 @@ void ProcessGestureEvent(GestureEvent event)
currentGesture = GESTURE_DRAG; currentGesture = GESTURE_DRAG;
} }
} }
dragVector.x = moveDownPosition.x - touchDownDragPosition.x; dragVector.x = moveDownPosition.x - touchDownDragPosition.x;
dragVector.y = moveDownPosition.y - touchDownDragPosition.y; dragVector.y = moveDownPosition.y - touchDownDragPosition.y;
} }
@ -350,28 +350,28 @@ void ProcessGestureEvent(GestureEvent event)
{ {
touchDownPosition = event.position[0]; touchDownPosition = event.position[0];
touchDownPosition2 = event.position[1]; touchDownPosition2 = event.position[1];
//pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2); //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2);
pinchVector.x = touchDownPosition2.x - touchDownPosition.x; pinchVector.x = touchDownPosition2.x - touchDownPosition.x;
pinchVector.y = touchDownPosition2.y - touchDownPosition.y; pinchVector.y = touchDownPosition2.y - touchDownPosition.y;
currentGesture = GESTURE_HOLD; currentGesture = GESTURE_HOLD;
timeHold = GetCurrentTime(); timeHold = GetCurrentTime();
} }
else if (event.touchAction == TOUCH_MOVE) else if (event.touchAction == TOUCH_MOVE)
{ {
pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2); pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2);
touchDownPosition = moveDownPosition; touchDownPosition = moveDownPosition;
touchDownPosition2 = moveDownPosition2; touchDownPosition2 = moveDownPosition2;
moveDownPosition = event.position[0]; moveDownPosition = event.position[0];
moveDownPosition2 = event.position[1]; moveDownPosition2 = event.position[1];
pinchVector.x = moveDownPosition2.x - moveDownPosition.x; pinchVector.x = moveDownPosition2.x - moveDownPosition.x;
pinchVector.y = moveDownPosition2.y - moveDownPosition.y; pinchVector.y = moveDownPosition2.y - moveDownPosition.y;
if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH)) if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH))
{ {
if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN; if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN;
@ -382,7 +382,7 @@ void ProcessGestureEvent(GestureEvent event)
currentGesture = GESTURE_HOLD; currentGesture = GESTURE_HOLD;
timeHold = GetCurrentTime(); timeHold = GetCurrentTime();
} }
// NOTE: Angle should be inverted in Y // NOTE: Angle should be inverted in Y
pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2); pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2);
} }
@ -392,7 +392,7 @@ void ProcessGestureEvent(GestureEvent event)
pinchAngle = 0.0f; pinchAngle = 0.0f;
pinchVector = (Vector2){ 0.0f, 0.0f }; pinchVector = (Vector2){ 0.0f, 0.0f };
pointCount = 0; pointCount = 0;
currentGesture = GESTURE_NONE; currentGesture = GESTURE_NONE;
} }
} }
@ -409,14 +409,14 @@ void UpdateGestures(void)
currentGesture = GESTURE_HOLD; currentGesture = GESTURE_HOLD;
timeHold = GetCurrentTime(); timeHold = GetCurrentTime();
} }
if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2)) if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2))
{ {
currentGesture = GESTURE_HOLD; currentGesture = GESTURE_HOLD;
timeHold = GetCurrentTime(); timeHold = GetCurrentTime();
resetHold = true; resetHold = true;
} }
// Detect GESTURE_NONE // Detect GESTURE_NONE
if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN)) if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN))
{ {
@ -428,7 +428,7 @@ void UpdateGestures(void)
int GetTouchPointsCount(void) int GetTouchPointsCount(void)
{ {
// NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
return pointCount; return pointCount;
} }
@ -443,11 +443,11 @@ int GetGestureDetected(void)
float GetGestureHoldDuration(void) float GetGestureHoldDuration(void)
{ {
// NOTE: time is calculated on current gesture HOLD // NOTE: time is calculated on current gesture HOLD
double time = 0.0; double time = 0.0;
if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold; if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold;
return (float)time; return (float)time;
} }
@ -455,7 +455,7 @@ float GetGestureHoldDuration(void)
Vector2 GetGestureDragVector(void) Vector2 GetGestureDragVector(void)
{ {
// NOTE: drag vector is calculated on one touch points TOUCH_MOVE // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
return dragVector; return dragVector;
} }
@ -464,7 +464,7 @@ Vector2 GetGestureDragVector(void)
float GetGestureDragAngle(void) float GetGestureDragAngle(void)
{ {
// NOTE: drag angle is calculated on one touch points TOUCH_UP // NOTE: drag angle is calculated on one touch points TOUCH_UP
return dragAngle; return dragAngle;
} }
@ -473,7 +473,7 @@ Vector2 GetGesturePinchVector(void)
{ {
// NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index) // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
// NOTE: pinch distance is calculated on two touch points TOUCH_MOVE // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
return pinchVector; return pinchVector;
} }
@ -482,7 +482,7 @@ Vector2 GetGesturePinchVector(void)
float GetGesturePinchAngle(void) float GetGesturePinchAngle(void)
{ {
// NOTE: pinch angle is calculated on two touch points TOUCH_MOVE // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
return pinchAngle; return pinchAngle;
} }
@ -494,7 +494,7 @@ float GetGesturePinchAngle(void)
static float Vector2Angle(Vector2 v1, Vector2 v2) static float Vector2Angle(Vector2 v1, Vector2 v2)
{ {
float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI);
if (angle < 0) angle += 360.0f; if (angle < 0) angle += 360.0f;
return angle; return angle;
@ -518,13 +518,13 @@ static float Vector2Distance(Vector2 v1, Vector2 v2)
static double GetCurrentTime(void) static double GetCurrentTime(void)
{ {
double time = 0; double time = 0;
#if defined(_WIN32) #if defined(_WIN32)
unsigned long long int clockFrequency, currentTime; unsigned long long int clockFrequency, currentTime;
QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation!
QueryPerformanceCounter(&currentTime); QueryPerformanceCounter(&currentTime);
time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds
#endif #endif
@ -533,24 +533,24 @@ static double GetCurrentTime(void)
struct timespec now; struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds
time = ((double)nowTime/1000000.0); // Time in miliseconds time = ((double)nowTime/1000000.0); // Time in miliseconds
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
//#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01 //#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01
//#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time //#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time
clock_serv_t cclock; clock_serv_t cclock;
mach_timespec_t now; mach_timespec_t now;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
// NOTE: OS X does not have clock_gettime(), using clock_get_time() // NOTE: OS X does not have clock_gettime(), using clock_get_time()
clock_get_time(cclock, &now); clock_get_time(cclock, &now);
mach_port_deallocate(mach_task_self(), cclock); mach_port_deallocate(mach_task_self(), cclock);
uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds
time = ((double)nowTime/1000000.0); // Time in miliseconds time = ((double)nowTime/1000000.0); // Time in miliseconds
#endif #endif
return time; return time;

View File

@ -2385,8 +2385,18 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
for (int i = 0; i < model.meshCount; i++) for (int i = 0; i < model.meshCount; i++)
{ {
model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; // TODO: Review color + tint premultiplication mechanism
Color color = model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color;
Color colorTint = WHITE;
colorTint.r = (((float)color.r/255.0)*((float)tint.r/255.0))*255;
colorTint.g = (((float)color.g/255.0)*((float)tint.g/255.0))*255;
colorTint.b = (((float)color.b/255.0)*((float)tint.b/255.0))*255;
colorTint.a = (((float)color.a/255.0)*((float)tint.a/255.0))*255;
model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = colorTint;
rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = color;
} }
} }
@ -3344,6 +3354,88 @@ static unsigned char *DecodeBase64(char *input, int *size)
return buf; return buf;
} }
// Load texture from cgltf_image
static Texture LoadTextureFromCgltfImage(cgltf_image *image, const char *texPath, Color tint)
{
Texture texture = { 0 };
if (image->uri)
{
if ((strlen(image->uri) > 5) &&
(image->uri[0] == 'd') &&
(image->uri[1] == 'a') &&
(image->uri[2] == 't') &&
(image->uri[3] == 'a') &&
(image->uri[4] == ':'))
{
// Data URI
// Format: data:<mediatype>;base64,<data>
// Find the comma
int i = 0;
while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++;
if (image->uri[i] == 0) TraceLog(LOG_WARNING, "CGLTF Image: Invalid data URI");
else
{
int size;
unsigned char *data = DecodeBase64(image->uri + i + 1, &size);
int w, h;
unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4);
Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
// TODO: Tint shouldn't be applied here!
ImageColorTint(&rimage, tint);
texture = LoadTextureFromImage(rimage);
UnloadImage(rimage);
}
}
else
{
Image rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri));
// TODO: Tint shouldn't be applied here!
ImageColorTint(&rimage, tint);
texture = LoadTextureFromImage(rimage);
UnloadImage(rimage);
}
}
else if (image->buffer_view)
{
unsigned char *data = RL_MALLOC(image->buffer_view->size);
int n = image->buffer_view->offset;
int stride = image->buffer_view->stride ? image->buffer_view->stride : 1;
for (int i = 0; i < image->buffer_view->size; i++)
{
data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n];
n += stride;
}
int w, h;
unsigned char *raw = stbi_load_from_memory(data, image->buffer_view->size, &w, &h, NULL, 4);
free(data);
Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
free(raw);
// TODO: Tint shouldn't be applied here!
ImageColorTint(&rimage, tint);
texture = LoadTextureFromImage(rimage);
UnloadImage(rimage);
}
else
{
Image rimage = LoadImageEx(&tint, 1, 1);
texture = LoadTextureFromImage(rimage);
UnloadImage(rimage);
}
return texture;
}
// Load glTF mesh data // Load glTF mesh data
static Model LoadGLTF(const char *fileName) static Model LoadGLTF(const char *fileName)
{ {
@ -3409,9 +3501,7 @@ static Model LoadGLTF(const char *fileName)
// Read data buffers // Read data buffers
result = cgltf_load_buffers(&options, data, fileName); result = cgltf_load_buffers(&options, data, fileName);
if (result != cgltf_result_success) { if (result != cgltf_result_success) TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf");
TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf");
}
int primitivesCount = 0; int primitivesCount = 0;
@ -3426,106 +3516,55 @@ static Model LoadGLTF(const char *fileName)
for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
//For each material
for (int i = 0; i < model.materialCount - 1; i++) for (int i = 0; i < model.materialCount - 1; i++)
{ {
Color tint = WHITE; model.materials[i] = LoadMaterialDefault();
Texture2D texture = { 0 }; Color tint = (Color){ 255, 255, 255, 255 };
const char *texPath = GetDirectoryPath(fileName); const char *texPath = GetDirectoryPath(fileName);
if (data->materials[i].has_pbr_metallic_roughness) //Ensure material follows raylib support for PBR (metallic/roughness flow)
if (data->materials[i].has_pbr_metallic_roughness)
{ {
tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255.99f); float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255.99f); float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255.99f);
tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255.99f);
}
else
{
tint.r = 1.0f;
tint.g = 1.0f;
tint.b = 1.0f;
tint.a = 1.0f;
}
if (data->materials[i].has_pbr_metallic_roughness) // NOTE: Material name not used for the moment
{ //if (model.materials[i].name && data->materials[i].name) strcpy(model.materials[i].name, data->materials[i].name);
cgltf_image *img = data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image;
if (img->uri) // TODO: REview: shouldn't these be *255 ???
tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
model.materials[i].maps[MAP_ROUGHNESS].color = tint;
if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture)
{ {
if ((strlen(img->uri) > 5) && model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint);
(img->uri[0] == 'd') &&
(img->uri[1] == 'a') &&
(img->uri[2] == 't') &&
(img->uri[3] == 'a') &&
(img->uri[4] == ':'))
{
// Data URI
// Format: data:<mediatype>;base64,<data>
// Find the comma
int i = 0;
while ((img->uri[i] != ',') && (img->uri[i] != 0)) i++;
if (img->uri[i] == 0) TraceLog(LOG_WARNING, "[%s] Invalid data URI", fileName);
else
{
int size;
unsigned char *data = DecodeBase64(img->uri + i + 1, &size);
int w, h;
unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4);
Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
ImageColorTint(&image, tint);
texture = LoadTextureFromImage(image);
UnloadImage(image);
}
}
else
{
char *textureName = img->uri;
char *texturePath = RL_MALLOC(strlen(texPath) + strlen(textureName) + 2);
strcpy(texturePath, texPath);
strcat(texturePath, "/");
strcat(texturePath, textureName);
Image image = LoadImage(texturePath);
ImageColorTint(&image, tint);
texture = LoadTextureFromImage(image);
UnloadImage(image);
RL_FREE(texturePath);
}
}
else if (img->buffer_view)
{
unsigned char *data = RL_MALLOC(img->buffer_view->size);
int n = img->buffer_view->offset;
int stride = img->buffer_view->stride ? img->buffer_view->stride : 1;
for (int i = 0; i < img->buffer_view->size; i++)
{
data[i] = ((unsigned char *)img->buffer_view->buffer->data)[n];
n += stride;
}
int w, h;
unsigned char *raw = stbi_load_from_memory(data, img->buffer_view->size, &w, &h, NULL, 4);
Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
ImageColorTint(&image, tint);
texture = LoadTextureFromImage(image);
UnloadImage(image);
}
else
{
Image image = LoadImageEx(&tint, 1, 1);
texture = LoadTextureFromImage(image);
UnloadImage(image);
} }
model.materials[i] = LoadMaterialDefault(); // NOTE: Tint isn't need for other textures.. pass null or clear?
model.materials[i].maps[MAP_DIFFUSE].texture = texture; // Just set as white, multiplying by white has no effect
tint = WHITE;
if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
{
model.materials[i].maps[MAP_ROUGHNESS].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint);
}
model.materials[i].maps[MAP_ROUGHNESS].value = roughness;
model.materials[i].maps[MAP_METALNESS].value = metallic;
if (data->materials[i].normal_texture.texture)
{
model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint);
}
if (data->materials[i].occlusion_texture.texture)
{
model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint);
}
} }
} }

View File

@ -1896,7 +1896,7 @@ static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3)
static void InitTimer(void) static void InitTimer(void)
{ {
srand(time(NULL)); // Initialize random seed srand(time(NULL)); // Initialize random seed
#if defined(_WIN32) #if defined(_WIN32)
QueryPerformanceFrequency((unsigned long long int *) &frequency); QueryPerformanceFrequency((unsigned long long int *) &frequency);
#endif #endif
@ -1911,7 +1911,7 @@ static void InitTimer(void)
mach_timebase_info(&timebase); mach_timebase_info(&timebase);
frequency = (timebase.denom*1e9)/timebase.numer; frequency = (timebase.denom*1e9)/timebase.numer;
#endif #endif
baseTime = GetTimeCount(); // Get MONOTONIC clock time offset baseTime = GetTimeCount(); // Get MONOTONIC clock time offset
startTime = GetCurrentTime(); // Get current time startTime = GetCurrentTime(); // Get current time
} }
@ -1920,7 +1920,7 @@ static void InitTimer(void)
static uint64_t GetTimeCount(void) static uint64_t GetTimeCount(void)
{ {
uint64_t value = 0; uint64_t value = 0;
#if defined(_WIN32) #if defined(_WIN32)
QueryPerformanceCounter((unsigned long long int *) &value); QueryPerformanceCounter((unsigned long long int *) &value);
#endif #endif

View File

@ -198,10 +198,10 @@ typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioB
// playback device depending on whether or not data is streamed // playback device depending on whether or not data is streamed
struct rAudioBuffer { struct rAudioBuffer {
ma_pcm_converter dsp; // PCM data converter ma_pcm_converter dsp; // PCM data converter
float volume; // Audio buffer volume float volume; // Audio buffer volume
float pitch; // Audio buffer pitch float pitch; // Audio buffer pitch
bool playing; // Audio buffer state: AUDIO_PLAYING bool playing; // Audio buffer state: AUDIO_PLAYING
bool paused; // Audio buffer state: AUDIO_PAUSED bool paused; // Audio buffer state: AUDIO_PAUSED
bool looping; // Audio buffer looping, always true for AudioStreams bool looping; // Audio buffer looping, always true for AudioStreams
@ -209,11 +209,11 @@ struct rAudioBuffer {
bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
unsigned int frameCursorPos; // Frame cursor position unsigned int frameCursorPos; // Frame cursor position
unsigned int bufferSizeInFrames; // Total buffer size in frames unsigned int bufferSizeInFrames; // Total buffer size in frames
unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming) unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming)
unsigned char *buffer; // Data buffer, on music stream keeps filling unsigned char *buffer; // Data buffer, on music stream keeps filling
rAudioBuffer *next; // Next audio buffer on the list rAudioBuffer *next; // Next audio buffer on the list
rAudioBuffer *prev; // Previous audio buffer on the list rAudioBuffer *prev; // Previous audio buffer on the list
}; };
@ -289,7 +289,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
if (!audioBuffer->playing || audioBuffer->paused) continue; if (!audioBuffer->playing || audioBuffer->paused) continue;
ma_uint32 framesRead = 0; ma_uint32 framesRead = 0;
while (1) while (1)
{ {
if (framesRead > frameCount) if (framesRead > frameCount)
@ -302,7 +302,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
// Just read as much data as we can from the stream // Just read as much data as we can from the stream
ma_uint32 framesToRead = (frameCount - framesRead); ma_uint32 framesToRead = (frameCount - framesRead);
while (framesToRead > 0) while (framesToRead > 0)
{ {
float tempBuffer[1024]; // 512 frames for stereo float tempBuffer[1024]; // 512 frames for stereo
@ -387,7 +387,7 @@ static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut,
{ {
if (framesRead >= frameCount) break; if (framesRead >= frameCount) break;
} }
else else
{ {
if (isSubBufferProcessed[currentSubBufferIndex]) break; if (isSubBufferProcessed[currentSubBufferIndex]) break;
} }
@ -465,7 +465,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
static void InitAudioBufferPool() static void InitAudioBufferPool()
{ {
// Dummy buffers // Dummy buffers
for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
{ {
audioBufferPool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC); audioBufferPool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC);
} }
@ -474,7 +474,11 @@ static void InitAudioBufferPool()
// Close the audio buffers pool // Close the audio buffers pool
static void CloseAudioBufferPool() static void CloseAudioBufferPool()
{ {
for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) RL_FREE(audioBufferPool[i]); for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
{
RL_FREE(audioBufferPool[i]->buffer);
RL_FREE(audioBufferPool[i]);
}
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -486,7 +490,7 @@ void InitAudioDevice(void)
// Init audio context // Init audio context
ma_context_config contextConfig = ma_context_config_init(); ma_context_config contextConfig = ma_context_config_init();
contextConfig.logCallback = OnLog; contextConfig.logCallback = OnLog;
ma_result result = ma_context_init(NULL, 0, &contextConfig, &context); ma_result result = ma_context_init(NULL, 0, &contextConfig, &context);
if (result != MA_SUCCESS) if (result != MA_SUCCESS)
{ {
@ -589,7 +593,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
{ {
AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer)); AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer));
audioBuffer->buffer = RL_CALLOC(bufferSizeInFrames*channels*ma_get_bytes_per_sample(format), 1); audioBuffer->buffer = RL_CALLOC(bufferSizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
if (audioBuffer == NULL) if (audioBuffer == NULL)
{ {
TraceLog(LOG_ERROR, "InitAudioBuffer() : Failed to allocate memory for audio buffer"); TraceLog(LOG_ERROR, "InitAudioBuffer() : Failed to allocate memory for audio buffer");
@ -608,7 +612,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
dspConfig.onRead = OnAudioBufferDSPRead; // Callback on data reading dspConfig.onRead = OnAudioBufferDSPRead; // Callback on data reading
dspConfig.pUserData = audioBuffer; // Audio data pointer dspConfig.pUserData = audioBuffer; // Audio data pointer
dspConfig.allowDynamicSampleRate = true; // Required for pitch shifting dspConfig.allowDynamicSampleRate = true; // Required for pitch shifting
ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp); ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp);
if (result != MA_SUCCESS) if (result != MA_SUCCESS)
@ -655,7 +659,7 @@ void CloseAudioBuffer(AudioBuffer *buffer)
bool IsAudioBufferPlaying(AudioBuffer *buffer) bool IsAudioBufferPlaying(AudioBuffer *buffer)
{ {
bool result = false; bool result = false;
if (buffer != NULL) result = (buffer->playing && !buffer->paused); if (buffer != NULL) result = (buffer->playing && !buffer->paused);
else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer");
@ -698,7 +702,7 @@ void StopAudioBuffer(AudioBuffer *buffer)
void PauseAudioBuffer(AudioBuffer *buffer) void PauseAudioBuffer(AudioBuffer *buffer)
{ {
if (buffer != NULL) buffer->paused = true; if (buffer != NULL) buffer->paused = true;
else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer");
} }
// Resume an audio buffer // Resume an audio buffer
@ -722,8 +726,8 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
{ {
float pitchMul = pitch/buffer->pitch; float pitchMul = pitch/buffer->pitch;
// Pitching is just an adjustment of the sample rate. // Pitching is just an adjustment of the sample rate.
// Note that this changes the duration of the sound: // Note that this changes the duration of the sound:
// - higher pitches will make the sound faster // - higher pitches will make the sound faster
// - lower pitches make it slower // - lower pitches make it slower
ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul); ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul);
@ -816,7 +820,7 @@ Sound LoadSoundFromWave(Wave wave)
if (wave.data != NULL) if (wave.data != NULL)
{ {
// When using miniaudio we need to do our own mixing. // When using miniaudio we need to do our own mixing.
// To simplify this we need convert the format of each sound to be consistent with // To simplify this we need convert the format of each sound to be consistent with
// the format used to open the playback device. We can do this two ways: // the format used to open the playback device. We can do this two ways:
// //
@ -909,7 +913,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
FILE *txtFile = fopen(fileName, "wt"); FILE *txtFile = fopen(fileName, "wt");
if (txtFile != NULL) if (txtFile != NULL)
{ {
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
@ -967,7 +971,7 @@ void PlaySoundMulti(Sound sound)
oldAge = audioBufferPoolChannels[i]; oldAge = audioBufferPoolChannels[i];
oldIndex = i; oldIndex = i;
} }
if (!IsAudioBufferPlaying(audioBufferPool[i])) if (!IsAudioBufferPlaying(audioBufferPool[i]))
{ {
index = i; index = i;
@ -979,17 +983,17 @@ void PlaySoundMulti(Sound sound)
if (index == -1) if (index == -1)
{ {
TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", audioBufferPoolCounter); TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", audioBufferPoolCounter);
if (oldIndex == -1) if (oldIndex == -1)
{ {
// Shouldn't be able to get here... but just in case something odd happens! // Shouldn't be able to get here... but just in case something odd happens!
TraceLog(LOG_ERROR,"sound buffer pool couldn't determine oldest buffer not playing sound"); TraceLog(LOG_ERROR,"sound buffer pool couldn't determine oldest buffer not playing sound");
return; return;
} }
index = oldIndex; index = oldIndex;
// Just in case... // Just in case...
StopAudioBuffer(audioBufferPool[index]); StopAudioBuffer(audioBufferPool[index]);
} }
@ -1000,7 +1004,7 @@ void PlaySoundMulti(Sound sound)
audioBufferPoolChannels[index] = audioBufferPoolCounter; audioBufferPoolChannels[index] = audioBufferPoolCounter;
audioBufferPoolCounter++; audioBufferPoolCounter++;
audioBufferPool[index]->volume = sound.stream.buffer->volume; audioBufferPool[index]->volume = sound.stream.buffer->volume;
audioBufferPool[index]->pitch = sound.stream.buffer->pitch; audioBufferPool[index]->pitch = sound.stream.buffer->pitch;
audioBufferPool[index]->looping = sound.stream.buffer->looping; audioBufferPool[index]->looping = sound.stream.buffer->looping;
@ -1023,12 +1027,12 @@ void StopSoundMulti(void)
int GetSoundsPlaying(void) int GetSoundsPlaying(void)
{ {
int counter = 0; int counter = 0;
for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
{ {
if (IsAudioBufferPlaying(audioBufferPool[i])) counter++; if (IsAudioBufferPlaying(audioBufferPool[i])) counter++;
} }
return counter; return counter;
} }
@ -1211,7 +1215,7 @@ Music LoadMusicStream(const char *fileName)
{ {
drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3)); drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3));
music.ctxData = ctxMp3; music.ctxData = ctxMp3;
int result = drmp3_init_file(ctxMp3, fileName, NULL); int result = drmp3_init_file(ctxMp3, fileName, NULL);
if (result > 0) if (result > 0)
@ -1242,7 +1246,7 @@ Music LoadMusicStream(const char *fileName)
music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm);
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
music.ctxData = ctxXm; music.ctxData = ctxXm;
} }
} }
@ -1252,7 +1256,7 @@ Music LoadMusicStream(const char *fileName)
{ {
jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t)); jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t));
music.ctxData = ctxMod; music.ctxData = ctxMod;
jar_mod_init(ctxMod); jar_mod_init(ctxMod);
int result = jar_mod_load_file(ctxMod, fileName); int result = jar_mod_load_file(ctxMod, fileName);
@ -1335,7 +1339,7 @@ void PlayMusicStream(Music music)
{ {
// For music streams, we need to make sure we maintain the frame cursor position // For music streams, we need to make sure we maintain the frame cursor position
// This is a hack for this section of code in UpdateMusicStream() // This is a hack for this section of code in UpdateMusicStream()
// NOTE: In case window is minimized, music stream is stopped, just make sure to // NOTE: In case window is minimized, music stream is stopped, just make sure to
// play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music); // play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music);
ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; ma_uint32 frameCursorPos = audioBuffer->frameCursorPos;
PlayAudioStream(music.stream); // WARNING: This resets the cursor position. PlayAudioStream(music.stream); // WARNING: This resets the cursor position.
@ -1395,7 +1399,7 @@ void UpdateMusicStream(Music music)
void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1); void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1);
int samplesCount = 0; // Total size of data streamed in L+R samples for xm floats, individual L or R for ogg shorts int samplesCount = 0; // Total size of data streamed in L+R samples for xm floats, individual L or R for ogg shorts
// TODO: Get the sampleLeft using totalFramesProcessed... but first, get total frames processed correctly... // TODO: Get the sampleLeft using totalFramesProcessed... but first, get total frames processed correctly...
//ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels;
int sampleLeft = music.sampleCount - (music.stream.buffer->totalFramesProcessed*music.stream.channels); int sampleLeft = music.sampleCount - (music.stream.buffer->totalFramesProcessed*music.stream.channels);
@ -1449,7 +1453,7 @@ void UpdateMusicStream(Music music)
} }
UpdateAudioStream(music.stream, pcm, samplesCount); UpdateAudioStream(music.stream, pcm, samplesCount);
if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD))
{ {
if (samplesCount > 1) sampleLeft -= samplesCount/2; if (samplesCount > 1) sampleLeft -= samplesCount/2;
@ -1549,11 +1553,11 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
// 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.playback.internalBufferSizeInFrames/device.playback.internalPeriods; unsigned int periodSize = device.playback.internalBufferSizeInFrames/device.playback.internalPeriods;
unsigned int subBufferSize = AUDIO_BUFFER_SIZE; unsigned int subBufferSize = AUDIO_BUFFER_SIZE;
if (subBufferSize < periodSize) subBufferSize = periodSize; if (subBufferSize < periodSize) subBufferSize = periodSize;
stream.buffer = InitAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); stream.buffer = InitAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM);
if (stream.buffer != NULL) if (stream.buffer != NULL)
{ {
stream.buffer->looping = true; // Always loop for streaming buffers stream.buffer->looping = true; // Always loop for streaming buffers
@ -1578,7 +1582,7 @@ void CloseAudioStream(AudioStream stream)
void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
{ {
AudioBuffer *audioBuffer = stream.buffer; AudioBuffer *audioBuffer = stream.buffer;
if (audioBuffer != NULL) if (audioBuffer != NULL)
{ {
if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
@ -1587,7 +1591,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1])
{ {
// Both buffers are available for updating. // Both buffers are available for updating.
// Update the first one and make sure the cursor is moved back to the front. // Update the first one and make sure the cursor is moved back to the front.
subBufferToUpdate = 0; subBufferToUpdate = 0;
audioBuffer->frameCursorPos = 0; audioBuffer->frameCursorPos = 0;
@ -1604,7 +1608,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
// TODO: Get total frames processed on this buffer... DOES NOT WORK. // TODO: Get total frames processed on this buffer... DOES NOT WORK.
audioBuffer->totalFramesProcessed += subBufferSizeInFrames; audioBuffer->totalFramesProcessed += subBufferSizeInFrames;
// Does this API expect a whole buffer to be updated in one go? // Does this API expect a whole buffer to be updated in one go?
// Assuming so, but if not will need to change this logic. // Assuming so, but if not will need to change this logic.
if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels) if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels)
{ {

View File

@ -110,7 +110,7 @@ typedef struct Sound {
typedef struct Music { typedef struct Music {
int ctxType; // Type of music context (audio filetype) int ctxType; // Type of music context (audio filetype)
void *ctxData; // Audio context data, depends on type void *ctxData; // Audio context data, depends on type
unsigned int sampleCount; // Total number of samples unsigned int sampleCount; // Total number of samples
unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop

View File

@ -106,7 +106,7 @@
#ifndef RL_FREE #ifndef RL_FREE
#define RL_FREE(p) free(p) #define RL_FREE(p) free(p)
#endif #endif
// NOTE: MSC C++ compiler does not support compound literals (C99 feature) // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers. // Plain structures in C++ (without constructors) can be initialized from { } initializers.
#if defined(__cplusplus) #if defined(__cplusplus)
@ -432,7 +432,7 @@ typedef struct Sound {
typedef struct Music { typedef struct Music {
int ctxType; // Type of music context (audio filetype) int ctxType; // Type of music context (audio filetype)
void *ctxData; // Audio context data, depends on type void *ctxData; // Audio context data, depends on type
unsigned int sampleCount; // Total number of samples unsigned int sampleCount; // Total number of samples
unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop
@ -1188,14 +1188,11 @@ RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontS
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font
RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string
// NOTE: 0x3f('?') is returned on failure
// Text strings management functions // Text strings management functions (no utf8 strings, only byte chars)
// NOTE: Some strings allocate memory internally for returned strings, just be careful! // NOTE: Some strings allocate memory internally for returned strings, just be careful!
RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal
RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending
RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string
RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style)
RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string
RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!)
@ -1208,6 +1205,12 @@ RLAPI const char *TextToUpper(const char *text); // Get upp
RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string
RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string
RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported)
RLAPI const char *TextToUtf8(int codepoint, int *byteLength); // Encode codepoint into utf8 text (char array length returned as parameter)
// UTF8 text strings management functions
RLAPI int *GetCodepoints(const char *text, int *count); // Get all codepoints in a string, codepoints count returned by parameters
RLAPI int GetCodepointsCount(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string
RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string; 0x3f('?') is returned on failure
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models) // Basic 3d Shapes Drawing Functions (Module: models)
@ -1319,6 +1322,7 @@ RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D textur
RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix)
RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix)
RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix
RLAPI Matrix GetMatrixProjection(void); // Get internal projection matrix
// Texture maps generation (PBR) // Texture maps generation (PBR)
// NOTE: Required shaders should be provided // NOTE: Required shaders should be provided
@ -1406,7 +1410,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i
RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream
RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Network (Module: network) // Network (Module: network)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
* *
* rglfw - raylib GLFW single file compilation * rglfw - raylib GLFW single file compilation
* *
* This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together * This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together
* with raylib for all supported platforms, this way, no external dependencies are required. * with raylib for all supported platforms, this way, no external dependencies are required.
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng

View File

@ -2967,7 +2967,7 @@ char *LoadText(const char *fileName)
Shader LoadShader(const char *vsFileName, const char *fsFileName) Shader LoadShader(const char *vsFileName, const char *fsFileName)
{ {
Shader shader = { 0 }; Shader shader = { 0 };
// NOTE: Shader.locs is allocated by LoadShaderCode() // NOTE: Shader.locs is allocated by LoadShaderCode()
char *vShaderStr = NULL; char *vShaderStr = NULL;
@ -3155,6 +3155,23 @@ void SetMatrixProjection(Matrix proj)
#endif #endif
} }
// Return internal projection matrix
Matrix GetMatrixProjection(void) {
#if defined(GRAPHICS_API_OPENGL_11)
float mat[16];
glGetFloatv(GL_PROJECTION_MATRIX,mat);
Matrix m;
m.m0 = mat[0]; m.m1 = mat[1]; m.m2 = mat[2]; m.m3 = mat[3];
m.m4 = mat[4]; m.m5 = mat[5]; m.m6 = mat[6]; m.m7 = mat[7];
m.m8 = mat[8]; m.m9 = mat[9]; m.m10 = mat[10]; m.m11 = mat[11];
m.m12 = mat[12]; m.m13 = mat[13]; m.m14 = mat[14]; m.m15 = mat[15];
return m;
#else
return projection;
#endif
#
}
// Set a custom modelview matrix (replaces internal modelview matrix) // Set a custom modelview matrix (replaces internal modelview matrix)
void SetMatrixModelview(Matrix view) void SetMatrixModelview(Matrix view)
{ {
@ -3170,6 +3187,10 @@ Matrix GetMatrixModelview(void)
#if defined(GRAPHICS_API_OPENGL_11) #if defined(GRAPHICS_API_OPENGL_11)
float mat[16]; float mat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mat); glGetFloatv(GL_MODELVIEW_MATRIX, mat);
matrix.m0 = mat[0]; matrix.m1 = mat[1]; matrix.m2 = mat[2]; matrix.m3 = mat[3];
matrix.m4 = mat[4]; matrix.m5 = mat[5]; matrix.m6 = mat[6]; matrix.m7 = mat[7];
matrix.m8 = mat[8]; matrix.m9 = mat[9]; matrix.m10 = mat[10]; matrix.m11 = mat[11];
matrix.m12 = mat[12]; matrix.m13 = mat[13]; matrix.m14 = mat[14]; matrix.m15 = mat[15];
#else #else
matrix = modelview; matrix = modelview;
#endif #endif

View File

@ -54,7 +54,7 @@
#else #else
#define RMEMAPI // We are building or using library as a static library (or Linux shared library) #define RMEMAPI // We are building or using library as a static library (or Linux shared library)
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -139,9 +139,9 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref);
#if defined(RMEM_IMPLEMENTATION) #if defined(RMEM_IMPLEMENTATION)
#include <stdio.h> // Required for: #include <stdio.h> // Required for:
#include <stdlib.h> // Required for: #include <stdlib.h> // Required for:
#include <string.h> // Required for: #include <string.h> // Required for:
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
@ -163,8 +163,8 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static inline size_t __AlignSize(const size_t size, const size_t align) static inline size_t __AlignSize(const size_t size, const size_t align)
{ {
return (size + (align - 1)) & -align; return (size + (align - 1)) & -align;
} }
@ -175,9 +175,9 @@ static inline size_t __AlignSize(const size_t size, const size_t align)
MemPool CreateMemPool(const size_t size) MemPool CreateMemPool(const size_t size)
{ {
MemPool mempool = { 0 }; MemPool mempool = { 0 };
if (size == 0UL) return mempool; if (size == 0UL) return mempool;
else else
{ {
// Align the mempool size to at least the size of an alloc node. // Align the mempool size to at least the size of an alloc node.
mempool.stack.size = size; mempool.stack.size = size;
@ -188,7 +188,7 @@ MemPool CreateMemPool(const size_t size)
mempool.stack.size = 0UL; mempool.stack.size = 0UL;
return mempool; return mempool;
} }
else else
{ {
mempool.stack.base = mempool.stack.mem + mempool.stack.size; mempool.stack.base = mempool.stack.mem + mempool.stack.size;
return mempool; return mempool;
@ -199,9 +199,9 @@ MemPool CreateMemPool(const size_t size)
MemPool CreateMemPoolFromBuffer(void *buf, const size_t size) MemPool CreateMemPoolFromBuffer(void *buf, const size_t size)
{ {
MemPool mempool = { 0 }; MemPool mempool = { 0 };
if ((size == 0UL) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool; if ((size == 0UL) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool;
else else
{ {
mempool.stack.size = size; mempool.stack.size = size;
mempool.stack.mem = buf; mempool.stack.mem = buf;
@ -213,7 +213,7 @@ MemPool CreateMemPoolFromBuffer(void *buf, const size_t size)
void DestroyMemPool(MemPool *const mempool) void DestroyMemPool(MemPool *const mempool)
{ {
if ((mempool == NULL) || (mempool->stack.mem == NULL)) return; if ((mempool == NULL) || (mempool->stack.mem == NULL)) return;
else else
{ {
free(mempool->stack.mem); free(mempool->stack.mem);
*mempool = (MemPool){ 0 }; *mempool = (MemPool){ 0 };
@ -228,7 +228,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
MemNode *new_mem = NULL; MemNode *new_mem = NULL;
const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t)); const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t));
const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1; const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1;
// If the size is small enough, let's check if our buckets has a fitting memory block. // If the size is small enough, let's check if our buckets has a fitting memory block.
if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE) if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE)
{ {
@ -240,7 +240,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
else if (mempool->freeList.head != NULL) else if (mempool->freeList.head != NULL)
{ {
const size_t MEM_SPLIT_THRESHOLD = 16; const size_t MEM_SPLIT_THRESHOLD = 16;
// If the freelist is valid, let's allocate FROM the freelist then! // If the freelist is valid, let's allocate FROM the freelist then!
for (MemNode *inode = mempool->freeList.head; inode != NULL; inode = inode->next) for (MemNode *inode = mempool->freeList.head; inode != NULL; inode = inode->next)
{ {
@ -251,10 +251,10 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
new_mem = inode; new_mem = inode;
(inode->prev != NULL)? (inode->prev->next = inode->next) : (mempool->freeList.head = inode->next); (inode->prev != NULL)? (inode->prev->next = inode->next) : (mempool->freeList.head = inode->next);
(inode->next != NULL)? (inode->next->prev = inode->prev) : (mempool->freeList.tail = inode->prev); (inode->next != NULL)? (inode->next->prev = inode->prev) : (mempool->freeList.tail = inode->prev);
if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL;
else mempool->freeList.tail = NULL; else mempool->freeList.tail = NULL;
if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL;
mempool->freeList.len--; mempool->freeList.len--;
break; break;
@ -269,7 +269,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
} }
} }
} }
if (new_mem == NULL) if (new_mem == NULL)
{ {
// not enough memory to support the size! // not enough memory to support the size!
@ -279,13 +279,13 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
// Couldn't allocate from a freelist, allocate from available mempool. // Couldn't allocate from a freelist, allocate from available mempool.
// Subtract allocation size from the mempool. // Subtract allocation size from the mempool.
mempool->stack.base -= ALLOC_SIZE; mempool->stack.base -= ALLOC_SIZE;
// Use the available mempool space as the new node. // Use the available mempool space as the new node.
new_mem = (MemNode *)mempool->stack.base; new_mem = (MemNode *)mempool->stack.base;
new_mem->size = ALLOC_SIZE; new_mem->size = ALLOC_SIZE;
} }
} }
// Visual of the allocation block. // Visual of the allocation block.
// -------------- // --------------
// | mem size | lowest addr of block // | mem size | lowest addr of block
@ -314,7 +314,7 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si
MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node); MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node);
const size_t NODE_SIZE = sizeof *node; const size_t NODE_SIZE = sizeof *node;
uint8_t *const resized_block = MemPoolAlloc(mempool, size); uint8_t *const resized_block = MemPoolAlloc(mempool, size);
if (resized_block == NULL) return NULL; if (resized_block == NULL) return NULL;
else else
{ {
@ -329,16 +329,16 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si
void MemPoolFree(MemPool *const restrict mempool, void *ptr) void MemPoolFree(MemPool *const restrict mempool, void *ptr)
{ {
if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return; if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return;
else else
{ {
// Behind the actual pointer data is the allocation info. // Behind the actual pointer data is the allocation info.
MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node); MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node);
const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1; const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1;
// Make sure the pointer data is valid. // Make sure the pointer data is valid.
if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) ||
(((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) || (((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) ||
(mem_node->size == 0UL) || (mem_node->size == 0UL) ||
(mem_node->size > mempool->stack.size)) return; (mem_node->size > mempool->stack.size)) return;
// If the mem_node is right at the stack base ptr, then add it to the stack. // If the mem_node is right at the stack base ptr, then add it to the stack.
else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base) else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base)
@ -362,13 +362,13 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr)
else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/ else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/
{ {
for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return; for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return;
// This code insertion sorts where largest size is last. // This code insertion sorts where largest size is last.
if (mempool->freeList.head == NULL) if (mempool->freeList.head == NULL)
{ {
mempool->freeList.head = mempool->freeList.tail = mem_node; mempool->freeList.head = mempool->freeList.tail = mem_node;
mempool->freeList.len++; mempool->freeList.len++;
} }
else if (mempool->freeList.head->size >= mem_node->size) else if (mempool->freeList.head->size >= mem_node->size)
{ {
mem_node->next = mempool->freeList.head; mem_node->next = mempool->freeList.head;
@ -383,7 +383,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr)
mempool->freeList.tail = mem_node; mempool->freeList.tail = mem_node;
mempool->freeList.len++; mempool->freeList.len++;
} }
if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool); if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool);
} }
} }
@ -392,7 +392,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr)
void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref) void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref)
{ {
if ((mempool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return; if ((mempool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return;
else else
{ {
MemPoolFree(mempool, *ptrref); MemPoolFree(mempool, *ptrref);
*ptrref = NULL; *ptrref = NULL;
@ -402,11 +402,11 @@ void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref)
size_t GetMemPoolFreeMemory(const MemPool mempool) size_t GetMemPoolFreeMemory(const MemPool mempool)
{ {
size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem; size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem;
for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size; for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size;
for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++) for (MemNode *n = mempool.buckets[i]; n != NULL; n = n->next) total_remaining += n->size; for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++) for (MemNode *n = mempool.buckets[i]; n != NULL; n = n->next) total_remaining += n->size;
return total_remaining; return total_remaining;
} }
@ -423,12 +423,12 @@ bool MemPoolDefrag(MemPool *const mempool)
for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL; for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL;
mempool->stack.base = mempool->stack.mem + mempool->stack.size; mempool->stack.base = mempool->stack.mem + mempool->stack.size;
return true; return true;
} }
else else
{ {
for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++) for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++)
{ {
while (mempool->buckets[i] != NULL) while (mempool->buckets[i] != NULL)
{ {
if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base) if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base)
{ {
@ -440,42 +440,42 @@ bool MemPoolDefrag(MemPool *const mempool)
else break; else break;
} }
} }
const size_t PRE_DEFRAG_LEN = mempool->freeList.len; const size_t PRE_DEFRAG_LEN = mempool->freeList.len;
MemNode **node = &mempool->freeList.head; MemNode **node = &mempool->freeList.head;
while (*node != NULL) while (*node != NULL)
{ {
if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) if ((uintptr_t)*node == (uintptr_t)mempool->stack.base)
{ {
// If node is right at the stack, merge it back into the stack. // If node is right at the stack, merge it back into the stack.
mempool->stack.base += (*node)->size; mempool->stack.base += (*node)->size;
(*node)->size = 0UL; (*node)->size = 0UL;
((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (mempool->freeList.head = (*node)->next); ((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (mempool->freeList.head = (*node)->next);
((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (mempool->freeList.tail = (*node)->prev); ((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (mempool->freeList.tail = (*node)->prev);
if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL;
else mempool->freeList.tail = NULL; else mempool->freeList.tail = NULL;
if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL;
mempool->freeList.len--; mempool->freeList.len--;
node = &mempool->freeList.head; node = &mempool->freeList.head;
} }
else if (((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->next) else if (((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->next)
{ {
// Next node is at a higher address. // Next node is at a higher address.
(*node)->size += (*node)->next->size; (*node)->size += (*node)->next->size;
(*node)->next->size = 0UL; (*node)->next->size = 0UL;
// <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]-> // <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]->
// //
// |--------------------| // |--------------------|
// <-[P Curr N]-> <-[P Next N]-> [P NextNext N]-> // <-[P Curr N]-> <-[P Next N]-> [P NextNext N]->
if ((*node)->next->next != NULL) (*node)->next->next->prev = *node; if ((*node)->next->next != NULL) (*node)->next->next->prev = *node;
// <-[P Curr N]-> <-[P NextNext N]-> // <-[P Curr N]-> <-[P NextNext N]->
(*node)->next = (*node)->next->next; (*node)->next = (*node)->next->next;
mempool->freeList.len--; mempool->freeList.len--;
node = &mempool->freeList.head; node = &mempool->freeList.head;
} }
@ -484,16 +484,16 @@ bool MemPoolDefrag(MemPool *const mempool)
// Prev node is at a higher address. // Prev node is at a higher address.
(*node)->size += (*node)->prev->size; (*node)->size += (*node)->prev->size;
(*node)->prev->size = 0UL; (*node)->prev->size = 0UL;
// <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]-> // <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]->
// //
// |--------------------| // |--------------------|
// <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]-> // <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]->
(*node)->prev->prev->next = *node; (*node)->prev->prev->next = *node;
// <-[P PrevPrev N]-> <-[P Curr N]-> // <-[P PrevPrev N]-> <-[P Curr N]->
(*node)->prev = (*node)->prev->prev; (*node)->prev = (*node)->prev->prev;
mempool->freeList.len--; mempool->freeList.len--;
node = &mempool->freeList.head; node = &mempool->freeList.head;
} }
@ -501,12 +501,12 @@ bool MemPoolDefrag(MemPool *const mempool)
{ {
// Next node is at a lower address. // Next node is at a lower address.
(*node)->next->size += (*node)->size; (*node)->next->size += (*node)->size;
(*node)->size = 0UL; (*node)->size = 0UL;
(*node)->next->prev = (*node)->prev; (*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next; (*node)->prev->next = (*node)->next;
*node = (*node)->next; *node = (*node)->next;
mempool->freeList.len--; mempool->freeList.len--;
node = &mempool->freeList.head; node = &mempool->freeList.head;
} }
@ -514,21 +514,21 @@ bool MemPoolDefrag(MemPool *const mempool)
{ {
// Prev node is at a lower address. // Prev node is at a lower address.
(*node)->prev->size += (*node)->size; (*node)->prev->size += (*node)->size;
(*node)->size = 0UL; (*node)->size = 0UL;
(*node)->next->prev = (*node)->prev; (*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next; (*node)->prev->next = (*node)->next;
*node = (*node)->prev; *node = (*node)->prev;
mempool->freeList.len--; mempool->freeList.len--;
node = &mempool->freeList.head; node = &mempool->freeList.head;
} }
else else
{ {
node = &(*node)->next; node = &(*node)->next;
} }
} }
return PRE_DEFRAG_LEN > mempool->freeList.len; return PRE_DEFRAG_LEN > mempool->freeList.len;
} }
} }
@ -551,19 +551,19 @@ union ObjInfo {
ObjPool CreateObjPool(const size_t objsize, const size_t len) ObjPool CreateObjPool(const size_t objsize, const size_t len)
{ {
ObjPool objpool = { 0 }; ObjPool objpool = { 0 };
if ((len == 0UL) || (objsize == 0UL)) return objpool; if ((len == 0UL) || (objsize == 0UL)) return objpool;
else else
{ {
objpool.objSize = __AlignSize(objsize, sizeof(size_t)); objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len; objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize); objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize);
if (objpool.stack.mem == NULL) if (objpool.stack.mem == NULL)
{ {
objpool.stack.size = 0UL; objpool.stack.size = 0UL;
return objpool; return objpool;
} }
else else
{ {
for (size_t i=0; i<objpool.freeBlocks; i++) for (size_t i=0; i<objpool.freeBlocks; i++)
@ -571,7 +571,7 @@ ObjPool CreateObjPool(const size_t objsize, const size_t len)
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] }; union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.index = i + 1; *block.index = i + 1;
} }
objpool.stack.base = objpool.stack.mem; objpool.stack.base = objpool.stack.mem;
return objpool; return objpool;
} }
@ -581,7 +581,7 @@ ObjPool CreateObjPool(const size_t objsize, const size_t len)
ObjPool CreateObjPoolFromBuffer(void *const buf, const size_t objsize, const size_t len) ObjPool CreateObjPoolFromBuffer(void *const buf, const size_t objsize, const size_t len)
{ {
ObjPool objpool = { 0 }; ObjPool objpool = { 0 };
// If the object size isn't large enough to align to a size_t, then we can't use it. // If the object size isn't large enough to align to a size_t, then we can't use it.
if ((buf == NULL) || (len == 0UL) || (objsize < sizeof(size_t)) || (objsize*len != __AlignSize(objsize, sizeof(size_t))*len)) return objpool; if ((buf == NULL) || (len == 0UL) || (objsize < sizeof(size_t)) || (objsize*len != __AlignSize(objsize, sizeof(size_t))*len)) return objpool;
else else
@ -589,13 +589,13 @@ ObjPool CreateObjPoolFromBuffer(void *const buf, const size_t objsize, const siz
objpool.objSize = __AlignSize(objsize, sizeof(size_t)); objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len; objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = buf; objpool.stack.mem = buf;
for (size_t i=0; i<objpool.freeBlocks; i++) for (size_t i=0; i<objpool.freeBlocks; i++)
{ {
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] }; union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.index = i + 1; *block.index = i + 1;
} }
objpool.stack.base = objpool.stack.mem; objpool.stack.base = objpool.stack.mem;
return objpool; return objpool;
} }
@ -623,7 +623,7 @@ void *ObjPoolAlloc(ObjPool *const objpool)
// ret = Head == ret = &pool[0]; // ret = Head == ret = &pool[0];
union ObjInfo ret = { .byte = objpool->stack.base }; union ObjInfo ret = { .byte = objpool->stack.base };
objpool->freeBlocks--; objpool->freeBlocks--;
// after allocating, we set head to the address of the index that *Head holds. // after allocating, we set head to the address of the index that *Head holds.
// Head = &pool[*Head * pool.objsize]; // Head = &pool[*Head * pool.objsize];
objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL; objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL;

View File

@ -95,10 +95,10 @@
// Platform type definitions // Platform type definitions
// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#ifdef WIN32 #ifdef WIN32
typedef int socklen_t; typedef int socklen_t;
#endif #endif
#ifndef RESULT_SUCCESS #ifndef RESULT_SUCCESS
# define RESULT_SUCCESS 0 # define RESULT_SUCCESS 0
@ -171,7 +171,7 @@ typedef int socklen_t;
#define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size #define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size
#define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options #define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options
#define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels #define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels
#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses #define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses
// Network address related defines // Network address related defines
@ -386,7 +386,7 @@ int AddSocket(SocketSet *set, Socket *sock);
int RemoveSocket(SocketSet *set, Socket *sock); int RemoveSocket(SocketSet *set, Socket *sock);
int CheckSockets(SocketSet *set, unsigned int timeout); int CheckSockets(SocketSet *set, unsigned int timeout);
// Packet API // Packet API
void PacketSend(Packet *packet); void PacketSend(Packet *packet);
void PacketReceive(Packet *packet); void PacketReceive(Packet *packet);
void PacketWrite8(Packet *packet, uint16_t value); void PacketWrite8(Packet *packet, uint16_t value);

View File

@ -10,9 +10,19 @@
* supported by default, to remove support, just comment unrequired #define in this module * supported by default, to remove support, just comment unrequired #define in this module
* *
* #define SUPPORT_DEFAULT_FONT * #define SUPPORT_DEFAULT_FONT
* Load default raylib font on initialization to be used by DrawText() and MeasureText().
* If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
*
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size
*
* #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer)
*
* *
* DEPENDENCIES: * DEPENDENCIES:
* stb_truetype - Load TTF file and rasterize characters data * stb_truetype - Load TTF file and rasterize characters data
* stb_rect_pack - Rectangles packing algorythms, required for font atlas generation
* *
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
@ -40,7 +50,7 @@
// Check if config flags have been externally provided on compilation line // Check if config flags have been externally provided on compilation line
#if !defined(EXTERNAL_CONFIG_FLAGS) #if !defined(EXTERNAL_CONFIG_FLAGS)
#include "config.h" // Defines module configuration flags #include "config.h" // Defines module configuration flags
#endif #endif
#include <stdlib.h> // Required for: malloc(), free() #include <stdlib.h> // Required for: malloc(), free()
@ -63,7 +73,18 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers of some Text*() functions #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
// TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal()
#define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints
#if !defined(TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH)
#define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 1024 // Size of static buffer: TextSplit()
#endif
#if !defined(TEXTSPLIT_MAX_SUBSTRINGS_COUNT)
#define TEXTSPLIT_MAX_SUBSTRINGS_COUNT 128 // Size of static pointers array: TextSplit()
#endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
@ -282,7 +303,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 }; Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF) #if defined(SUPPORT_FILEFORMAT_TTF)
if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS);
else else
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FNT) #if defined(SUPPORT_FILEFORMAT_FNT)
@ -321,7 +342,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
{ {
Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0); Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0);
font.texture = LoadTextureFromImage(atlas); font.texture = LoadTextureFromImage(atlas);
// Update chars[i].image to use alpha, required to be used on ImageDrawText() // Update chars[i].image to use alpha, required to be used on ImageDrawText()
for (int i = 0; i < font.charsCount; i++) for (int i = 0; i < font.charsCount; i++)
{ {
@ -439,7 +460,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
for (int i = 0; i < spriteFont.charsCount; i++) for (int i = 0; i < spriteFont.charsCount; i++)
{ {
spriteFont.chars[i].value = tempCharValues[i]; spriteFont.chars[i].value = tempCharValues[i];
// Get character rectangle in the font atlas texture // Get character rectangle in the font atlas texture
spriteFont.recs[i] = tempCharRecs[i]; spriteFont.recs[i] = tempCharRecs[i];
@ -447,7 +468,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;
// Fill character image data from fontClear data // Fill character image data from fontClear data
spriteFont.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]); spriteFont.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]);
} }
@ -587,7 +608,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
// 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;
// NOTE: Rectangles memory is loaded here! // NOTE: Rectangles memory is loaded here!
Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle)); Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
@ -711,7 +732,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
RL_FREE(atlas.data); RL_FREE(atlas.data);
atlas.data = dataGrayAlpha; atlas.data = dataGrayAlpha;
atlas.format = UNCOMPRESSED_GRAY_ALPHA; atlas.format = UNCOMPRESSED_GRAY_ALPHA;
*charRecs = recs; *charRecs = recs;
return atlas; return atlas;
@ -756,120 +777,6 @@ void DrawFPS(int posX, int posY)
DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME); DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME);
} }
// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
// Total number of bytes processed are returned as a parameter
// NOTE: the standard says U+FFFD should be returned in case of errors
// but that character is not supported by the default font in raylib
// TODO: optimize this code for speed!!
int GetNextCodepoint(const char *text, int *bytesProcessed)
{
/*
UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt
Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
// NOTE: on decode errors we return as soon as possible
int code = 0x3f; // Codepoint (defaults to '?')
int octet = (unsigned char)(text[0]); // The first UTF8 octet
*bytesProcessed = 1;
if (octet <= 0x7f)
{
// Only one octet (ASCII range x00-7F)
code = text[0];
}
else if ((octet & 0xe0) == 0xc0)
{
// Two octets
// [0]xC2-DF [1]UTF8-tail(x80-BF)
unsigned char octet1 = text[1];
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
if ((octet >= 0xc2) && (octet <= 0xdf))
{
code = ((octet & 0x1f) << 6) | (octet1 & 0x3f);
*bytesProcessed = 2;
}
}
else if ((octet & 0xf0) == 0xe0)
{
// Three octets
unsigned char octet1 = text[1];
unsigned char octet2 = '\0';
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
octet2 = text[2];
if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
/*
[0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF)
[0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF)
[0]xED [1]x80-9F [2]UTF8-tail(x80-BF)
[0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF)
*/
if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
if ((octet >= 0xe0) && (0 <= 0xef))
{
code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
*bytesProcessed = 3;
}
}
else if ((octet & 0xf8) == 0xf0)
{
// Four octets
if (octet > 0xf4) return code;
unsigned char octet1 = text[1];
unsigned char octet2 = '\0';
unsigned char octet3 = '\0';
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
octet2 = text[2];
if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
octet3 = text[3];
if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence
/*
[0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail
[0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail
[0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail
*/
if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) ||
((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence
if (octet >= 0xf0)
{
code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f);
*bytesProcessed = 4;
}
}
if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid
return code;
}
// Draw text (using default font) // Draw text (using default font)
// NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
// NOTE: chars spacing is proportional to fontSize // NOTE: chars spacing is proportional to fontSize
@ -907,12 +814,12 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int next = 0; int next = 0;
letter = GetNextCodepoint(&text[i], &next); letter = GetNextCodepoint(&text[i], &next);
index = GetGlyphIndex(font, letter); index = GetGlyphIndex(font, letter);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set 'next = 1' // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set 'next = 1'
if (letter == 0x3f) next = 1; if (letter == 0x3f) next = 1;
i += (next - 1); i += (next - 1);
if (letter == '\n') if (letter == '\n')
{ {
// NOTE: Fixed line spacing of 1.5 lines // NOTE: Fixed line spacing of 1.5 lines
@ -960,17 +867,17 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
int startLine = -1; // Index where to begin drawing (where a line begins) int startLine = -1; // Index where to begin drawing (where a line begins)
int endLine = -1; // Index where to stop drawing (where a line ends) int endLine = -1; // Index where to stop drawing (where a line ends)
int lastk = -1; // Holds last value of the character position int lastk = -1; // Holds last value of the character position
for (int i = 0, k = 0; i < length; i++, k++) for (int i = 0, k = 0; i < length; i++, k++)
{ {
int glyphWidth = 0; int glyphWidth = 0;
int next = 0; int next = 0;
letter = GetNextCodepoint(&text[i], &next); letter = GetNextCodepoint(&text[i], &next);
index = GetGlyphIndex(font, letter); index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1; if (letter == 0x3f) next = 1;
i += next - 1; i += next - 1;
if (letter != '\n') if (letter != '\n')
@ -988,7 +895,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
if (state == MEASURE_STATE) if (state == MEASURE_STATE)
{ {
// TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more // TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
// See: http://jkorpela.fi/chars/spaces.html // See: http://jkorpela.fi/chars/spaces.html
if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i; if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i;
if ((textOffsetX + glyphWidth + 1) >= rec.width) if ((textOffsetX + glyphWidth + 1) >= rec.width)
@ -1013,7 +920,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
textOffsetX = 0; textOffsetX = 0;
i = startLine; i = startLine;
glyphWidth = 0; glyphWidth = 0;
// Save character position when we switch states // Save character position when we switch states
int tmp = lastk; int tmp = lastk;
lastk = k - 1; lastk = k - 1;
@ -1114,16 +1021,16 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
lenCounter++; lenCounter++;
int next = 0; int next = 0;
letter = GetNextCodepoint(&text[i], &next); letter = GetNextCodepoint(&text[i], &next);
index = GetGlyphIndex(font, letter); index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1; if (letter == 0x3f) next = 1;
i += next - 1; i += next - 1;
if (letter != '\n') if (letter != '\n')
{ {
if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX;
@ -1194,27 +1101,6 @@ unsigned int TextLength(const char *text)
return length; return length;
} }
// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found
// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead
unsigned int TextCountCodepoints(const char *text)
{
unsigned int len = 0;
char *ptr = (char *)&text[0];
while (*ptr != '\0')
{
int next = 0;
int letter = GetNextCodepoint(ptr, &next);
if (letter == 0x3f) ptr += 1;
else ptr += next;
len++;
}
return len;
}
// Formatting of text with variables to 'embed' // Formatting of text with variables to 'embed'
const char *TextFormat(const char *text, ...) const char *TextFormat(const char *text, ...)
{ {
@ -1338,14 +1224,14 @@ const char *TextJoin(const char **textList, int count, const char *delimiter)
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
int textListLength = strlen(textList[i]); int textListLength = strlen(textList[i]);
// Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
if ((totalLength + textListLength) < MAX_TEXT_BUFFER_LENGTH) if ((totalLength + textListLength) < MAX_TEXT_BUFFER_LENGTH)
{ {
strcat(text, textList[i]); strcat(text, textList[i]);
totalLength += textListLength; totalLength += textListLength;
if ((delimiterLen > 0) && (i < (count - 1))) if ((delimiterLen > 0) && (i < (count - 1)))
{ {
strcat(text, delimiter); strcat(text, delimiter);
totalLength += delimiterLen; totalLength += delimiterLen;
@ -1362,22 +1248,20 @@ const char **TextSplit(const char *text, char delimiter, int *count)
// NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
// inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
// all used memory is static... it has some limitations: // all used memory is static... it has some limitations:
// 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT // 1. Maximum number of possible split strings is set by TEXTSPLIT_MAX_SUBSTRINGS_COUNT
// 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
#define MAX_SUBSTRINGS_COUNT 64 static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = { NULL };
static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = { 0 };
static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL }; memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH);
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
result[0] = buffer; result[0] = buffer;
int counter = 0; int counter = 0;
if (text != NULL) if (text != NULL)
{ {
counter = 1; counter = 1;
// Count how many substrings we have on text and point to every one // 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++)
{ {
@ -1389,7 +1273,7 @@ const char **TextSplit(const char *text, char delimiter, int *count)
result[counter] = buffer + i + 1; result[counter] = buffer + i + 1;
counter++; counter++;
if (counter == MAX_SUBSTRINGS_COUNT) break; if (counter == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) break;
} }
} }
} }
@ -1492,6 +1376,200 @@ int TextToInteger(const char *text)
return result; return result;
} }
// Encode codepoint into utf8 text (char array length returned as parameter)
RLAPI const char *TextToUtf8(int codepoint, int *byteLength)
{
static char utf8[6] = { 0 };
int length = 0;
if (codepoint <= 0x7f)
{
utf8[0] = (char)codepoint;
length = 1;
}
else if (codepoint <= 0x7ff)
{
utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
utf8[1] = (char)((codepoint & 0x3f) | 0x80);
length = 2;
}
else if (codepoint <= 0xffff)
{
utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
utf8[2] = (char)((codepoint & 0x3f) | 0x80);
length = 3;
}
else if (codepoint <= 0x10ffff)
{
utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
utf8[3] = (char)((codepoint & 0x3f) | 0x80);
length = 4;
}
*byteLength = length;
return utf8;
}
// Get all codepoints in a string, codepoints count returned by parameters
int *GetCodepoints(const char *text, int *count)
{
static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 };
memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int));
int bytesProcessed = 0;
int textLength = strlen(text);
int codepointsCount = 0;
for (int i = 0; i < textLength; codepointsCount++)
{
codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed);
i += bytesProcessed;
}
*count = codepointsCount;
return codepoints;
}
// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found
// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead
int GetCodepointsCount(const char *text)
{
unsigned int len = 0;
char *ptr = (char *)&text[0];
while (*ptr != '\0')
{
int next = 0;
int letter = GetNextCodepoint(ptr, &next);
if (letter == 0x3f) ptr += 1;
else ptr += next;
len++;
}
return len;
}
// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
// Total number of bytes processed are returned as a parameter
// NOTE: the standard says U+FFFD should be returned in case of errors
// but that character is not supported by the default font in raylib
// TODO: optimize this code for speed!!
int GetNextCodepoint(const char *text, int *bytesProcessed)
{
/*
UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt
Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
// NOTE: on decode errors we return as soon as possible
int code = 0x3f; // Codepoint (defaults to '?')
int octet = (unsigned char)(text[0]); // The first UTF8 octet
*bytesProcessed = 1;
if (octet <= 0x7f)
{
// Only one octet (ASCII range x00-7F)
code = text[0];
}
else if ((octet & 0xe0) == 0xc0)
{
// Two octets
// [0]xC2-DF [1]UTF8-tail(x80-BF)
unsigned char octet1 = text[1];
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
if ((octet >= 0xc2) && (octet <= 0xdf))
{
code = ((octet & 0x1f) << 6) | (octet1 & 0x3f);
*bytesProcessed = 2;
}
}
else if ((octet & 0xf0) == 0xe0)
{
// Three octets
unsigned char octet1 = text[1];
unsigned char octet2 = '\0';
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
octet2 = text[2];
if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
/*
[0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF)
[0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF)
[0]xED [1]x80-9F [2]UTF8-tail(x80-BF)
[0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF)
*/
if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
if ((octet >= 0xe0) && (0 <= 0xef))
{
code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
*bytesProcessed = 3;
}
}
else if ((octet & 0xf8) == 0xf0)
{
// Four octets
if (octet > 0xf4) return code;
unsigned char octet1 = text[1];
unsigned char octet2 = '\0';
unsigned char octet3 = '\0';
if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
octet2 = text[2];
if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
octet3 = text[3];
if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence
/*
[0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail
[0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail
[0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail
*/
if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) ||
((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence
if (octet >= 0xf0)
{
code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f);
*bytesProcessed = 4;
}
}
if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid
return code;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -1572,18 +1650,15 @@ static Font LoadBMFont(const char *fileName)
TraceLog(LOG_DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); TraceLog(LOG_DEBUG, "[%s] Font texture loading path: %s", fileName, texPath);
Image imFont = LoadImage(texPath); Image imFont = LoadImage(texPath);
Image imFontAlpha = ImageCopy(imFont);
if (imFont.format == UNCOMPRESSED_GRAYSCALE) if (imFont.format == UNCOMPRESSED_GRAYSCALE)
{ {
for (int i = 0; i < imFontAlpha.width*imFontAlpha.height; i++) ((unsigned char *)imFontAlpha.data)[i] = 0xff; // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
ImageAlphaMask(&imFont, imFont);
ImageAlphaMask(&imFontAlpha, imFont); for (int p = 0; p < (imFont.width*imFont.height*2); p += 2) ((unsigned char *)(imFont.data))[p] = 0xff;
font.texture = LoadTextureFromImage(imFontAlpha);
} }
else font.texture = LoadTextureFromImage(imFont);
font.texture = LoadTextureFromImage(imFont);
UnloadImage(imFont);
RL_FREE(texPath); RL_FREE(texPath);
@ -1600,7 +1675,7 @@ static Font LoadBMFont(const char *fileName)
fgets(buffer, MAX_BUFFER_SIZE, fntFile); fgets(buffer, MAX_BUFFER_SIZE, fntFile);
sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
// Get character rectangle in the font atlas texture // Get character rectangle in the font atlas texture
font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
@ -1609,12 +1684,12 @@ 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;
// Fill character image data from imFont data // Fill character image data from imFont data
font.chars[i].image = ImageFromImage(imFontAlpha, font.recs[i]); font.chars[i].image = ImageFromImage(imFont, font.recs[i]);
} }
UnloadImage(imFontAlpha); UnloadImage(imFont);
fclose(fntFile); fclose(fntFile);

View File

@ -850,7 +850,7 @@ void ExportImageAsCode(Image image, const char *fileName)
#define BYTES_TEXT_PER_LINE 20 #define BYTES_TEXT_PER_LINE 20
FILE *txtFile = fopen(fileName, "wt"); FILE *txtFile = fopen(fileName, "wt");
if (txtFile != NULL) if (txtFile != NULL)
{ {
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
@ -926,11 +926,11 @@ Image ImageCopy(Image image)
Image ImageFromImage(Image image, Rectangle rec) Image ImageFromImage(Image image, Rectangle rec)
{ {
Image result = ImageCopy(image); Image result = ImageCopy(image);
#if defined(SUPPORT_IMAGE_MANIPULATION) #if defined(SUPPORT_IMAGE_MANIPULATION)
ImageCrop(&result, rec); ImageCrop(&result, rec);
#endif #endif
return result; return result;
} }
@ -1181,13 +1181,18 @@ void ImageAlphaMask(Image *image, Image alphaMask)
// In case image is only grayscale, we just add alpha channel // In case image is only grayscale, we just add alpha channel
if (image->format == UNCOMPRESSED_GRAYSCALE) if (image->format == UNCOMPRESSED_GRAYSCALE)
{ {
ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA); unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
// Apply alpha mask to alpha channel // Apply alpha mask to alpha channel
for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
{ {
((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; data[k] = ((unsigned char *)image->data)[i];
data[k + 1] = ((unsigned char *)mask.data)[i];
} }
RL_FREE(image->data);
image->data = data;
image->format = UNCOMPRESSED_GRAY_ALPHA;
} }
else else
{ {
@ -1338,13 +1343,13 @@ void ImageCrop(Image *image, Rectangle crop)
{ {
// Security check to avoid program crash // Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
// Security checks to validate crop rectangle // Security checks to validate crop rectangle
if (crop.x < 0) { crop.width += crop.x; crop.x = 0; } if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
if (crop.y < 0) { crop.height += crop.y; crop.y = 0; } if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x; if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y; if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
if ((crop.x < image->width) && (crop.y < image->height)) if ((crop.x < image->width) && (crop.y < image->height))
{ {
// Start the cropping process // Start the cropping process
@ -1818,7 +1823,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
} }
Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it
// Crop source image to desired source rectangle (if required) // Crop source image to desired source rectangle (if required)
if ((src.width != (int)srcRec.width) && (src.height != (int)srcRec.height)) ImageCrop(&srcCopy, srcRec); if ((src.width != (int)srcRec.width) && (src.height != (int)srcRec.height)) ImageCrop(&srcCopy, srcRec);
@ -1827,7 +1832,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
{ {
ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height);
} }
// Check that dstRec is inside dst image // Check that dstRec is inside dst image
// Allow negative position within destination with cropping // Allow negative position within destination with cropping
if (dstRec.x < 0) if (dstRec.x < 0)
@ -1836,7 +1841,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
dstRec.width = dstRec.width + dstRec.x; dstRec.width = dstRec.width + dstRec.x;
dstRec.x = 0; dstRec.x = 0;
} }
if ((dstRec.x + dstRec.width) > dst->width) if ((dstRec.x + dstRec.width) > dst->width)
{ {
ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height }); ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height });
@ -1849,7 +1854,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
dstRec.height = dstRec.height + dstRec.y; dstRec.height = dstRec.height + dstRec.y;
dstRec.y = 0; dstRec.y = 0;
} }
if ((dstRec.y + dstRec.height) > dst->height) if ((dstRec.y + dstRec.height) > dst->height)
{ {
ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y }); ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y });
@ -1875,7 +1880,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]); fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]);
fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]); fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]);
// Apply color tint to source image // Apply color tint to source image
fsrc.x *= ftint.x; fsrc.y *= ftint.y; fsrc.z *= ftint.z; fsrc.w *= ftint.w; fsrc.x *= ftint.x; fsrc.y *= ftint.y; fsrc.z *= ftint.z; fsrc.w *= ftint.w;
@ -1938,16 +1943,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// Create image to store text // Create image to store text
Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
int next = 0; int next = 0;
letter = GetNextCodepoint(&text[i], &next); letter = GetNextCodepoint(&text[i], &next);
index = GetGlyphIndex(font, letter); index = GetGlyphIndex(font, letter);
if (letter == 0x3f) next = 1; if (letter == 0x3f) next = 1;
i += (next - 1); i += (next - 1);
if (letter == '\n') if (letter == '\n')
{ {
// TODO: Support line break // TODO: Support line break
@ -1957,7 +1962,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
if (letter != ' ') if (letter != ' ')
{ {
ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height }, ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height },
(Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY, (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY,
font.chars[index].image.width, font.chars[index].image.height }, tint); font.chars[index].image.width, font.chars[index].image.height }, tint);
} }
@ -1997,7 +2002,7 @@ void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + rec.height - thick, rec.width, thick }, color);
} }
// Draw text (default font) within an image (destination) // Draw text (default font) within an image (destination)
@ -2712,9 +2717,9 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
{ {
float width = (float)texture.width; float width = (float)texture.width;
float height = (float)texture.height; float height = (float)texture.height;
bool flipX = false; bool flipX = false;
if (sourceRec.width < 0) { flipX = true; sourceRec.width *= -1; } if (sourceRec.width < 0) { flipX = true; sourceRec.width *= -1; }
if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
@ -2976,12 +2981,12 @@ static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays)
fseek(gifFile, 0L, SEEK_END); fseek(gifFile, 0L, SEEK_END);
int size = ftell(gifFile); int size = ftell(gifFile);
fseek(gifFile, 0L, SEEK_SET); fseek(gifFile, 0L, SEEK_SET);
unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char)); unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char));
fread(buffer, sizeof(char), size, gifFile); fread(buffer, sizeof(char), size, gifFile);
fclose(gifFile); // Close file pointer fclose(gifFile); // Close file pointer
int comp = 0; int comp = 0;
image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4); image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4);

View File

@ -36,7 +36,7 @@
// Some basic Defines // Some basic Defines
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(PLATFORM_ANDROID) #if defined(PLATFORM_ANDROID)
#define fopen(name, mode) android_fopen(name, mode) #define fopen(name, mode) android_fopen(name, mode)
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -86,8 +86,8 @@ typedef enum {
typedef struct UWPMessage { typedef struct UWPMessage {
UWPMessageType type; // Message type UWPMessageType type; // Message type
Vector2 paramVector0; // Vector parameters Vector2 paramVector0; // Vector parameters
int paramInt0; // Int parameter int paramInt0; // Int parameter
int paramInt1; // Int parameter int paramInt1; // Int parameter
char paramChar0; // Char parameters char paramChar0; // Char parameters