Merge master

This commit is contained in:
Tyler Bezera 2019-10-02 12:28:12 -07:00
commit 824bdec70b
40 changed files with 1059 additions and 503 deletions

View File

@ -385,9 +385,9 @@ EXAMPLES = \
shapes/shapes_draw_circle_sector \ shapes/shapes_draw_circle_sector \
shapes/shapes_draw_rectangle_rounded \ shapes/shapes_draw_rectangle_rounded \
text/text_raylib_fonts \ text/text_raylib_fonts \
text/text_sprite_fonts \ text/text_font_spritefont \
text/text_ttf_loading \ text/text_font_loading \
text/text_bmfont_ttf \ text/text_font_filters \
text/text_font_sdf \ text/text_font_sdf \
text/text_format_text \ text/text_format_text \
text/text_input_box \ text/text_input_box \
@ -420,8 +420,7 @@ EXAMPLES = \
models/models_material_pbr \ models/models_material_pbr \
models/models_mesh_generation \ models/models_mesh_generation \
models/models_mesh_picking \ models/models_mesh_picking \
models/models_obj_loading \ models/models_loading \
models/models_obj_viewer \
models/models_orthographic_projection \ models/models_orthographic_projection \
models/models_rlgl_solar_system \ models/models_rlgl_solar_system \
models/models_skybox \ models/models_skybox \
@ -438,6 +437,8 @@ EXAMPLES = \
shaders/shaders_julia_set \ shaders/shaders_julia_set \
shaders/shaders_eratosthenes \ shaders/shaders_eratosthenes \
shaders/shaders_basic_lighting \ shaders/shaders_basic_lighting \
shaders/shaders_fog \
shaders/shaders_simple \
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

@ -46,13 +46,13 @@ int main(void)
circles[i].radius = GetRandomValue(10, 40); circles[i].radius = GetRandomValue(10, 40);
circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius); circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius);
circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius); circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius);
circles[i].speed = (float)GetRandomValue(1, 100)/20000.0f; circles[i].speed = (float)GetRandomValue(1, 100)/2000.0f;
circles[i].color = colors[GetRandomValue(0, 13)]; circles[i].color = colors[GetRandomValue(0, 13)];
} }
Music xm = LoadMusicStream("resources/chiptun1.mod"); Music music = LoadMusicStream("resources/mini1111.xm");
PlayMusicStream(xm); PlayMusicStream(music);
float timePlayed = 0.0f; float timePlayed = 0.0f;
bool pause = false; bool pause = false;
@ -65,13 +65,13 @@ int main(void)
{ {
// Update // Update
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
UpdateMusicStream(xm); // Update music buffer with new stream data UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play) // Restart music playing (stop and play)
if (IsKeyPressed(KEY_SPACE)) if (IsKeyPressed(KEY_SPACE))
{ {
StopMusicStream(xm); StopMusicStream(music);
PlayMusicStream(xm); PlayMusicStream(music);
} }
// Pause/Resume music playing // Pause/Resume music playing
@ -79,12 +79,12 @@ int main(void)
{ {
pause = !pause; pause = !pause;
if (pause) PauseMusicStream(xm); if (pause) PauseMusicStream(music);
else ResumeMusicStream(xm); else ResumeMusicStream(music);
} }
// Get timePlayed scaled to bar dimensions // Get timePlayed scaled to bar dimensions
timePlayed = GetMusicTimePlayed(xm)/GetMusicTimeLength(xm)*(screenWidth - 40); timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*(screenWidth - 40);
// Color circles animation // Color circles animation
for (int i = MAX_CIRCLES - 1; (i >= 0) && !pause; i--) for (int i = MAX_CIRCLES - 1; (i >= 0) && !pause; i--)
@ -101,7 +101,7 @@ int main(void)
circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius); circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius);
circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius); circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius);
circles[i].color = colors[GetRandomValue(0, 13)]; circles[i].color = colors[GetRandomValue(0, 13)];
circles[i].speed = (float)GetRandomValue(1, 100)/20000.0f; circles[i].speed = (float)GetRandomValue(1, 100)/2000.0f;
} }
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -128,7 +128,7 @@ int main(void)
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
UnloadMusicStream(xm); // Unload music stream buffers from RAM UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)

View File

@ -1,8 +1,17 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [models] example - OBJ models viewer * raylib [models] example - Models loading
* *
* This example has been created using raylib 2.0 (www.raylib.com) * raylib supports multiple models file formats:
*
* - OBJ > Text file, must include vertex position-texcoords-normals information,
* if files references some .mtl materials file, it will be loaded (or try to)
* - GLTF > Modern text/binary file format, includes lot of information and it could
* also reference external files, raylib will try loading mesh and materials data
* - IQM > Binary file format including mesh vertex data but also animation data,
* raylib can load .iqm animations.
*
* This example has been created using raylib 2.6 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
@ -11,8 +20,6 @@
#include "raylib.h" #include "raylib.h"
#include <string.h> // Required for: strcpy()
int main(void) int main(void)
{ {
// Initialization // Initialization
@ -20,22 +27,30 @@ int main(void)
const int screenWidth = 800; const int screenWidth = 800;
const int screenHeight = 450; const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib example - obj viewer"); InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading");
// Define the camera to look into our 3d world // Define the camera to look into our 3d world
Camera camera = { { 30.0f, 30.0f, 30.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Camera camera = { 0 };
camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 10.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.type = CAMERA_PERSPECTIVE; // Camera mode type
Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Model model = LoadModel("resources/models/castle.obj"); // Load model
Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture
model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture
Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds
bool selected = false; // Selected object flag
// NOTE: bounds are calculated from the original size of the model,
// if model is scaled on drawing, bounds must be also scaled
SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode
char objFilename[64] = "turret.obj"; bool selected = false; // Selected object flag
SetTargetFPS(60); // Set our game to run at 60 frames-per-second SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
@ -45,34 +60,40 @@ int main(void)
{ {
// Update // Update
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
UpdateCamera(&camera);
// Load new models/textures on drag&drop
if (IsFileDropped()) if (IsFileDropped())
{ {
int count = 0; int count = 0;
char **droppedFiles = GetDroppedFiles(&count); char **droppedFiles = GetDroppedFiles(&count);
if (count == 1) if (count == 1) // Only support one file dropped
{ {
if (IsFileExtension(droppedFiles[0], ".obj")) if (IsFileExtension(droppedFiles[0], ".obj") ||
IsFileExtension(droppedFiles[0], ".gltf") ||
IsFileExtension(droppedFiles[0], ".iqm")) // Model file formats supported
{ {
for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]); UnloadModel(model); // Unload previous model
model.meshes = LoadMeshes(droppedFiles[0], &model.meshCount); model = LoadModel(droppedFiles[0]); // Load new model
model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set current map diffuse texture
bounds = MeshBoundingBox(model.meshes[0]); bounds = MeshBoundingBox(model.meshes[0]);
// TODO: Move camera position from target enough distance to visualize model properly
} }
else if (IsFileExtension(droppedFiles[0], ".png")) else if (IsFileExtension(droppedFiles[0], ".png")) // Texture file formats supported
{ {
// Unload current model texture and load new one
UnloadTexture(texture); UnloadTexture(texture);
texture = LoadTexture(droppedFiles[0]); texture = LoadTexture(droppedFiles[0]);
model.materials[0].maps[MAP_DIFFUSE].texture = texture; model.materials[0].maps[MAP_DIFFUSE].texture = texture;
} }
strcpy(objFilename, GetFileName(droppedFiles[0]));
} }
ClearDroppedFiles(); // Clear internal buffers ClearDroppedFiles(); // Clear internal buffers
} }
UpdateCamera(&camera);
// Select model on mouse click // Select model on mouse click
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{ {
@ -90,25 +111,20 @@ int main(void)
BeginMode3D(camera); BeginMode3D(camera);
DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture
DrawGrid(20.0, 10.0); // Draw a grid DrawGrid(20, 10.0f); // Draw a grid
if (selected) DrawBoundingBox(bounds, GREEN); if (selected) DrawBoundingBox(bounds, GREEN); // Draw selection box
EndMode3D(); EndMode3D();
DrawText("Free camera default controls:", 10, 20, 10, DARKGRAY); DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY);
DrawText("- Mouse Wheel to Zoom in-out", 20, 40, 10, GRAY);
DrawText("- Mouse Wheel Pressed to Pan", 20, 60, 10, GRAY);
DrawText("- Alt + Mouse Wheel Pressed to Rotate", 20, 80, 10, GRAY);
DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 20, 100, 10, GRAY);
DrawText("Drag & drop .obj/.png to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY);
DrawText(FormatText("Current file: %s", objFilename), 250, GetScreenHeight() - 20, 10, GRAY);
if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN); if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN);
DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY);
DrawFPS(10, 10);
EndDrawing(); EndDrawing();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -119,8 +135,6 @@ int main(void)
UnloadTexture(texture); // Unload texture UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model UnloadModel(model); // Unload model
ClearDroppedFiles(); // Clear internal buffers
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

View File

@ -50,16 +50,15 @@ int main(void)
// NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO
MeshTangents(&model.meshes[0]); MeshTangents(&model.meshes[0]);
UnloadMaterial(model.materials[0]); // get rid of default material
model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f);
// Define lights attributes // Create lights
// NOTE: Shader is passed to every light on creation to define shader bindings internally // NOTE: Lights are added to an internal lights pool automatically
Light lights[MAX_LIGHTS] = { CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader);
CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader), CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader);
CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader), CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader);
CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader), CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader);
CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader)
};
SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode
@ -100,7 +99,19 @@ int main(void)
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
UnloadMaterial(model.materials[0]); // Unload material: shader and textures
// Shaders and textures must be unloaded by user,
// they could be in use by other models
UnloadTexture(model.materials[0].maps[MAP_ALBEDO].texture);
UnloadTexture(model.materials[0].maps[MAP_NORMAL].texture);
UnloadTexture(model.materials[0].maps[MAP_METALNESS].texture);
UnloadTexture(model.materials[0].maps[MAP_ROUGHNESS].texture);
UnloadTexture(model.materials[0].maps[MAP_OCCLUSION].texture);
UnloadTexture(model.materials[0].maps[MAP_IRRADIANCE].texture);
UnloadTexture(model.materials[0].maps[MAP_PREFILTER].texture);
UnloadTexture(model.materials[0].maps[MAP_BRDF].texture);
UnloadShader(model.materials[0].shader);
UnloadModel(model); // Unload model UnloadModel(model); // Unload model
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
@ -113,8 +124,8 @@ int main(void)
// NOTE: PBR shader is loaded inside this function // NOTE: PBR shader is loaded inside this function
static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) static Material LoadMaterialPBR(Color albedo, float metalness, float roughness)
{ {
Material mat = { 0 }; // NOTE: All maps textures are set to { 0 } Material mat = LoadMaterialDefault(); // Initialize material to default
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
mat.shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs"); mat.shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs");
#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
@ -136,7 +147,7 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness)
// Set view matrix location // Set view matrix location
mat.shader.locs[LOC_MATRIX_MODEL] = GetShaderLocation(mat.shader, "matModel"); mat.shader.locs[LOC_MATRIX_MODEL] = GetShaderLocation(mat.shader, "matModel");
mat.shader.locs[LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view"); //mat.shader.locs[LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view");
mat.shader.locs[LOC_VECTOR_VIEW] = GetShaderLocation(mat.shader, "viewPos"); mat.shader.locs[LOC_VECTOR_VIEW] = GetShaderLocation(mat.shader, "viewPos");
// Set PBR standard maps // Set PBR standard maps

View File

@ -1,80 +0,0 @@
/*******************************************************************************************
*
* raylib [models] example - Load and draw a 3d model (OBJ)
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - obj model loading");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 2.5f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.type = CAMERA_PERSPECTIVE; // Camera mode type
Model model = LoadModel("resources/models/castle.obj"); // Load OBJ model
Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture
model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
//...
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawModel(model, position, 0.2f, WHITE); // Draw 3d model with texture
DrawGrid(10, 1.0f); // Draw a grid
DrawGizmo(position); // Draw gizmo
EndMode3D();
DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

View File

@ -0,0 +1,219 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
2,
1
],
"matrix": [
0.009999999776482582,
0.0,
0.0,
0.0,
0.0,
0.009999999776482582,
0.0,
0.0,
0.0,
0.0,
0.009999999776482582,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"matrix": [
-0.7289686799049377,
0.0,
-0.6845470666885376,
0.0,
-0.4252049028873444,
0.7836934328079224,
0.4527972936630249,
0.0,
0.5364750623703003,
0.6211478114128113,
-0.571287989616394,
0.0,
400.1130065917969,
463.2640075683594,
-431.0780334472656,
1.0
],
"camera": 0
},
{
"mesh": 0
}
],
"cameras": [
{
"perspective": {
"aspectRatio": 1.5,
"yfov": 0.6605925559997559,
"zfar": 10000.0,
"znear": 1.0
},
"type": "perspective"
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "LOD3spShape"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 12636,
"max": [
2398
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 2399,
"max": [
0.9995989799499512,
0.999580979347229,
0.9984359741210938
],
"min": [
-0.9990839958190918,
-1.0,
-0.9998319745063782
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 28788,
"componentType": 5126,
"count": 2399,
"max": [
96.17990112304688,
163.97000122070313,
53.92519760131836
],
"min": [
-69.29850006103516,
9.929369926452637,
-61.32819747924805
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 2399,
"max": [
0.9833459854125976,
0.9800369739532472
],
"min": [
0.026409000158309938,
0.01996302604675293
],
"type": "VEC2"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
},
"emissiveFactor": [
0.0,
0.0,
0.0
],
"name": "blinn3-fx"
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
],
"images": [
{
"uri": "DuckCM.png"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9986,
"wrapS": 10497,
"wrapT": 10497
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 76768,
"byteLength": 25272,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 57576,
"byteStride": 12,
"target": 34962
},
{
"buffer": 0,
"byteOffset": 57576,
"byteLength": 19192,
"byteStride": 8,
"target": 34962
}
],
"buffers": [
{
"byteLength": 102040,
"uri": "Duck0.bin"
}
]
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,14 @@
# Duck
## Screenshot
![screenshot](screenshot/screenshot.png)
## License Information
Copyright 2006 Sony Computer Entertainment Inc.
Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:
http://research.scea.com/scea_shared_source_license.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -73,6 +73,8 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0);
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness); vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness);
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir); vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir);
// WARNING: There is some weird behaviour with this function, always returns black!
// Yes, I even tried: return texture(property.sampler, texCoord).rgb;
vec3 ComputeMaterialProperty(MaterialProperty property) vec3 ComputeMaterialProperty(MaterialProperty property)
{ {
vec3 result = vec3(0.0, 0.0, 0.0); vec3 result = vec3(0.0, 0.0, 0.0);
@ -187,17 +189,17 @@ void main()
else texCoord = fragTexCoord; // Use default texture coordinates else texCoord = fragTexCoord; // Use default texture coordinates
// Fetch material values from texture sampler or color attributes // Fetch material values from texture sampler or color attributes
vec3 color = ComputeMaterialProperty(albedo); vec3 color = texture(albedo.sampler, texCoord).rgb; //ComputeMaterialProperty(albedo);
vec3 metal = ComputeMaterialProperty(metalness); vec3 metal = texture(metalness.sampler, texCoord).rgb; //ComputeMaterialProperty(metalness);
vec3 rough = ComputeMaterialProperty(roughness); vec3 rough = texture(roughness.sampler, texCoord).rgb; //ComputeMaterialProperty(roughness);
vec3 emiss = ComputeMaterialProperty(emission); vec3 emiss = texture(emission.sampler, texCoord).rgb; //ComputeMaterialProperty(emission);
vec3 ao = ComputeMaterialProperty(occlusion); vec3 ao = texture(occlusion.sampler, texCoord).rgb; //ComputeMaterialProperty(occlusion);
// Check if normal mapping is enabled // Check if normal mapping is enabled
if (normals.useSampler == 1) if (normals.useSampler == 1)
{ {
// Fetch normal map color and transform lighting values to tangent space // Fetch normal map color and transform lighting values to tangent space
normal = ComputeMaterialProperty(normals); normal = texture(normals.sampler, texCoord).rgb; //ComputeMaterialProperty(normals);
normal = normalize(normal*2.0 - 1.0); normal = normalize(normal*2.0 - 1.0);
normal = normalize(normal*TBN); normal = normalize(normal*TBN);

View File

@ -33,6 +33,8 @@
#ifndef RLIGHTS_H #ifndef RLIGHTS_H
#define RLIGHTS_H #define RLIGHTS_H
#include "raylib.h"
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -65,16 +67,11 @@ typedef struct {
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
int lightsCount = 0; // Current amount of created lights
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Declaration // Module Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader
void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values
#ifdef __cplusplus #ifdef __cplusplus
} }
@ -106,7 +103,8 @@ void UpdateLightValues(Shader shader, Light light);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// ... static Light lights[MAX_LIGHTS] = { 0 };
static int lightsCount = 0; // Current amount of created lights
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
@ -118,7 +116,7 @@ void UpdateLightValues(Shader shader, Light light);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines a light and get locations from PBR shader // Defines a light and get locations from PBR shader
Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader) void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader)
{ {
Light light = { 0 }; Light light = { 0 };
@ -148,10 +146,10 @@ Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shade
light.colorLoc = GetShaderLocation(shader, colorName); light.colorLoc = GetShaderLocation(shader, colorName);
UpdateLightValues(shader, light); UpdateLightValues(shader, light);
lights[lightsCount] = light;
lightsCount++; lightsCount++;
} }
return light;
} }
// Send to PBR shader light values // Send to PBR shader light values

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

View File

@ -0,0 +1,21 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
// Input uniform values
uniform sampler2D texture0;
uniform sampler2D mask;
uniform int frame;
// Output fragment color
out vec4 finalColor;
void main()
{
vec4 maskColour = texture(mask, fragTexCoord+vec2(sin(-frame/150.0)/10.0,cos(-frame/170.0)/10.0));
if (maskColour.r < 0.25) discard;
vec4 texelColor = texture(texture0, fragTexCoord+vec2(sin(frame/90.0)/8.0,cos(frame/60.0)/8.0));
finalColor = texelColor * maskColour;
}

View File

@ -0,0 +1,21 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
// Output vertex attributes (to fragment shader)
out vec2 fragTexCoord;
void main()
{
// Send vertex attributes to fragment shader
fragTexCoord = vertexTexCoord;
// Calculate final vertex position
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

View File

@ -0,0 +1,139 @@
/*******************************************************************************************
*
* raylib [shaders] example - Simple shader mask
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
*
********************************************************************************************
*
* After a model is loaded it has a default material, this material can be
* modified in place rather than creating one from scratch...
* While all of the maps have particular names, they can be used for any purpose
* except for three maps that are applied as cubic maps (see below)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib - simple shader mask");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 0.0f, 1.0f, 2.0f };
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.type = CAMERA_PERSPECTIVE;
// Define our three models to show the shader on
Mesh torus = GenMeshTorus(.3, 1, 16, 32);
Model model1 = LoadModelFromMesh(torus);
Mesh cube = GenMeshCube(.8,.8,.8);
Model model2 = LoadModelFromMesh(cube);
// Generate model to be shaded just to see the gaps in the other two
Mesh sphere = GenMeshSphere(1, 16, 16);
Model model3 = LoadModelFromMesh(sphere);
// Load the shader
Shader shader = LoadShader("resources/shaders/glsl330/mask.vs", "resources/shaders/glsl330/mask.fs");
// Load and apply the diffuse texture (colour map)
Texture texDiffuse = LoadTexture("resources/plasma.png");
model1.materials[0].maps[MAP_DIFFUSE].texture = texDiffuse;
model2.materials[0].maps[MAP_DIFFUSE].texture = texDiffuse;
// Using MAP_EMISSION as a spare slot to use for 2nd texture
// NOTE: Don't use MAP_IRRADIANCE, MAP_PREFILTER or MAP_CUBEMAP
// as they are bound as cube maps
Texture texMask = LoadTexture("resources/mask.png");
model1.materials[0].maps[MAP_EMISSION].texture = texMask;
model2.materials[0].maps[MAP_EMISSION].texture = texMask;
shader.locs[LOC_MAP_EMISSION] = GetShaderLocation(shader, "mask");
// Frame is incremented each frame to animate the shader
int shaderFrame = GetShaderLocation(shader, "framesCounter");
// Apply the shader to the two models
model1.materials[0].shader = shader;
model2.materials[0].shader = shader;
int framesCounter = 0;
Vector3 rotation = { 0 }; // Model rotation angles
SetTargetFPS(60); // Set to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
framesCounter++;
rotation.x += 0.01f;
rotation.y += 0.005f;
rotation.z -= 0.0025f;
// Send frames counter to shader for animation
SetShaderValue(shader, shaderFrame, &framesCounter, UNIFORM_INT);
// Rotate one of the models
model1.transform = MatrixRotateXYZ(rotation);
UpdateCamera(&camera);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(DARKBLUE);
BeginMode3D(camera);
DrawModel(model1, (Vector3){0.5,0,0}, 1, WHITE);
DrawModelEx(model2, (Vector3){-.5,0,0}, (Vector3){1,1,0}, 50, (Vector3){1,1,1}, WHITE);
DrawModel(model3,(Vector3){0,0,-1.5}, 1, WHITE);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawRectangle(16, 698, MeasureText(FormatText("Frame: %i", framesCounter), 20) + 8, 42, BLUE);
DrawText(FormatText("Frame: %i", framesCounter), 20, 700, 20, WHITE);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model1);
UnloadModel(model2);
UnloadModel(model3);
UnloadTexture(texDiffuse); // Unload default diffuse texture
UnloadTexture(texMask); // Unload texture mask
UnloadShader(shader); // Unload shader
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

View File

@ -1,6 +1,10 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [text] example - TTF loading and usage * raylib [text] example - Font filters
*
* After font loading, font texture atlas filter could be configured for a softer
* display of the font when scaling it to different sizes, that way, it's not required
* to generate multiple fonts at multiple sizes (as long as the scaling is not very different)
* *
* This example has been created using raylib 1.3.0 (www.raylib.com) * This example has been created using raylib 1.3.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
@ -18,9 +22,9 @@ int main(void)
const int screenWidth = 800; const int screenWidth = 800;
const int screenHeight = 450; const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - ttf loading"); InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters");
const char msg[50] = "TTF Font"; const char msg[50] = "Loaded Font";
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
@ -78,7 +82,8 @@ int main(void)
int count = 0; int count = 0;
char **droppedFiles = GetDroppedFiles(&count); char **droppedFiles = GetDroppedFiles(&count);
if (count == 1) // Only support one ttf file dropped // NOTE: We only support first ttf file dropped
if (IsFileExtension(droppedFiles[0], ".ttf"))
{ {
UnloadFont(font); UnloadFont(font);
font = LoadFontEx(droppedFiles[0], fontSize, 0, 0); font = LoadFontEx(droppedFiles[0], fontSize, 0, 0);

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,11 +1,20 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [text] example - BMFont and TTF Fonts loading * raylib [text] example - Font loading
* *
* This example has been created using raylib 1.4 (www.raylib.com) * raylib can load fonts from multiple file formats:
*
* - TTF/OTF > Sprite font atlas is generated on loading, user can configure
* some of the generation parameters (size, characters to include)
* - BMFonts > Angel code font fileformat, sprite font image must be provided
* together with the .fnt file, font generation cna not be configured
* - XNA Spritefont > Sprite font image, following XNA Spritefont conventions,
* Characters in image must follow some spacing and order rules
*
* This example has been created using raylib 2.6 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2016 Ramon Santamaria (@raysan5) * Copyright (c) 2016-2019 Ramon Santamaria (@raysan5)
* *
********************************************************************************************/ ********************************************************************************************/
@ -18,7 +27,7 @@ int main(void)
const int screenWidth = 800; const int screenWidth = 800;
const int screenHeight = 450; const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont and ttf sprite fonts loading"); InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading");
// Define characters to draw // Define characters to draw
// NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally
@ -75,7 +84,7 @@ int main(void)
UnloadFont(fontBm); // AngelCode Font unloading UnloadFont(fontBm); // AngelCode Font unloading
UnloadFont(fontTtf); // TTF Font unloading UnloadFont(fontTtf); // TTF Font unloading
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
return 0; return 0;

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,6 +1,15 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [text] example - Font loading and usage * raylib [text] example - Sprite font loading
*
* Loaded sprite fonts have been generated following XNA SpriteFont conventions:
* - Characters must be ordered starting with character 32 (Space)
* - Every character must be contained within the same Rectangle height
* - Every character and every line must be separated the same distance
* - Rectangles must be defined by a MAGENTA color background
*
* If following this constraints, a font can be provided just by an image,
* this is quite handy to avoid additional information files (like BMFonts use).
* *
* This example has been created using raylib 1.0 (www.raylib.com) * This example has been created using raylib 1.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
@ -18,7 +27,7 @@ int main(void)
const int screenWidth = 800; const int screenWidth = 800;
const int screenHeight = 450; const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts usage"); InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite font loading");
const char msg1[50] = "THIS IS A custom SPRITE FONT..."; const char msg1[50] = "THIS IS A custom SPRITE FONT...";
const char msg2[50] = "...and this is ANOTHER CUSTOM font..."; const char msg2[50] = "...and this is ANOTHER CUSTOM font...";

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@ -147,13 +147,12 @@ endif
ifeq ($(PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM),PLATFORM_WEB)
# Emscripten required variables # Emscripten required variables
EMSDK_PATH ?= C:/emsdk EMSDK_PATH ?= C:/emsdk
EMSCRIPTEN_VERSION ?= 1.38.32 EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/fastcomp/emscripten
CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit CLANG_PATH = $(EMSDK_PATH)/fastcomp/bin
PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 PYTHON_PATH = $(EMSDK_PATH)/python/2.7.13.1_64bit/python-2.7.13.amd64
NODE_VERSION = 8.9.1_64bit NODE_PATH = $(EMSDK_PATH)/node/12.9.1_64bit/bin
export PATH = $(EMSDK_PATH);$(EMSDK_PATH)\clang\$(CLANG_VERSION);$(EMSDK_PATH)\node\$(NODE_VERSION)\bin;$(EMSDK_PATH)\python\$(PYTHON_VERSION);$(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION);C:\raylib\MinGW\bin:$$(PATH) export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH)
EMSCRIPTEN = $(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION)
endif endif
ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(PLATFORM),PLATFORM_ANDROID)
@ -278,7 +277,13 @@ endif
# -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec
# -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers
# -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing)
CFLAGS += -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing CFLAGS += -Wall -D_DEFAULT_SOURCE -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing
ifeq ($(PLATFORM), PLATFORM_WEB)
CFLAGS += -std=gnu99
else
CFLAGS += -std=c99
endif
ifeq ($(PLATFORM_OS),LINUX) ifeq ($(PLATFORM_OS),LINUX)
CFLAGS += -fPIC CFLAGS += -fPIC

View File

@ -56,7 +56,8 @@
#define SUPPORT_GIF_RECORDING 1 #define SUPPORT_GIF_RECORDING 1
// Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) // Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP)
//#define SUPPORT_HIGH_DPI 1 //#define SUPPORT_HIGH_DPI 1
// Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: rlgl - Configuration Flags // Module: rlgl - Configuration Flags
@ -85,10 +86,10 @@
//#define SUPPORT_FILEFORMAT_JPG 1 //#define SUPPORT_FILEFORMAT_JPG 1
//#define SUPPORT_FILEFORMAT_GIF 1 //#define SUPPORT_FILEFORMAT_GIF 1
//#define SUPPORT_FILEFORMAT_PSD 1 //#define SUPPORT_FILEFORMAT_PSD 1
#define SUPPORT_FILEFORMAT_DDS 1 //#define SUPPORT_FILEFORMAT_DDS 1
#define SUPPORT_FILEFORMAT_HDR 1 #define SUPPORT_FILEFORMAT_HDR 1
#define SUPPORT_FILEFORMAT_KTX 1 //#define SUPPORT_FILEFORMAT_KTX 1
#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_ASTC 1
//#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PKM 1
//#define SUPPORT_FILEFORMAT_PVR 1 //#define SUPPORT_FILEFORMAT_PVR 1
@ -133,8 +134,8 @@
#define SUPPORT_FILEFORMAT_OGG 1 #define SUPPORT_FILEFORMAT_OGG 1
#define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_XM 1
#define SUPPORT_FILEFORMAT_MOD 1 #define SUPPORT_FILEFORMAT_MOD 1
//#define SUPPORT_FILEFORMAT_FLAC 1 #define SUPPORT_FILEFORMAT_FLAC 1
#define SUPPORT_FILEFORMAT_MP3 1 #define SUPPORT_FILEFORMAT_MP3 1
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------

View File

@ -19,6 +19,8 @@
#cmakedefine SUPPORT_GIF_RECORDING 1 #cmakedefine SUPPORT_GIF_RECORDING 1
// Support high DPI displays // Support high DPI displays
#cmakedefine SUPPORT_HIGH_DPI 1 #cmakedefine SUPPORT_HIGH_DPI 1
// Support CompressData() and DecompressData() functions
#cmakedefine SUPPORT_COMPRESSION_API 1
// rlgl.h // rlgl.h
// Support VR simulation functionality (stereo rendering) // Support VR simulation functionality (stereo rendering)

View File

@ -74,6 +74,11 @@
* Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) * Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP)
* NOTE: This flag is forced on macOS, since most displays are high-DPI * NOTE: This flag is forced on macOS, since most displays are high-DPI
* *
* #define SUPPORT_COMPRESSION_API
* Support CompressData() and DecompressData() functions, those functions use zlib implementation
* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
* for linkage
*
* DEPENDENCIES: * DEPENDENCIES:
* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) * rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly)
* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
@ -252,6 +257,12 @@
#include <emscripten/html5.h> // Emscripten HTML5 library #include <emscripten/html5.h> // Emscripten HTML5 library
#endif #endif
#if defined(SUPPORT_COMPRESSION_API)
// NOTE: Those declarations require stb_image and stb_image_write definitions, included in textures module
unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality);
char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen);
#endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -1053,6 +1064,17 @@ int GetMonitorPhysicalHeight(int monitor)
return 0; return 0;
} }
// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
int x = 0;
int y = 0;
#if defined(PLATFORM_DESKTOP)
glfwGetWindowPos(window, &x, &y);
#endif
return (Vector2){ (float)x, (float)y };
}
// Get the human-readable, UTF-8 encoded name of the primary monitor // Get the human-readable, UTF-8 encoded name of the primary monitor
const char *GetMonitorName(int monitor) const char *GetMonitorName(int monitor)
{ {
@ -1838,9 +1860,10 @@ static const char *strprbrk(const char *s, const char *charset)
// Get pointer to filename for a path string // Get pointer to filename for a path string
const char *GetFileName(const char *filePath) const char *GetFileName(const char *filePath)
{ {
const char *fileName = strprbrk(filePath, "\\/"); const char *fileName = NULL;
if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
if (!fileName || fileName == filePath) return filePath; if (!fileName || (fileName == filePath)) return filePath;
return fileName + 1; return fileName + 1;
} }
@ -1853,7 +1876,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH]; static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH];
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
strcpy(fileName, GetFileName(filePath)); // Get filename with extension if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
int len = strlen(fileName); int len = strlen(fileName);
@ -2018,6 +2041,32 @@ long GetFileModTime(const char *fileName)
return 0; return 0;
} }
// Compress data (DEFLATE algorythm)
unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength)
{
#define COMPRESSION_QUALITY_DEFLATE 8
unsigned char *compData = NULL;
#if defined(SUPPORT_COMPRESSION_API)
compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE);
#endif
return compData;
}
// Decompress data (DEFLATE algorythm)
unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength)
{
char *data = NULL;
#if defined(SUPPORT_COMPRESSION_API)
data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength);
#endif
return (unsigned char *)data;
}
// Save integer value to storage file (to defined position) // Save integer value to storage file (to defined position)
// NOTE: Storage positions is directly related to file memory layout (4 bytes each integer) // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
void StorageSaveValue(int position, int value) void StorageSaveValue(int position, int value)
@ -3911,7 +3960,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified)
} }
// GLFW3 Window Drop Callback, runs when drop files into window // GLFW3 Window Drop Callback, runs when drop files into window
// NOTE: Paths are stored in dinamic memory for further retrieval // NOTE: Paths are stored in dynamic memory for further retrieval
// Everytime new files are dropped, old ones are discarded // Everytime new files are dropped, old ones are discarded
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
{ {

View File

@ -453,6 +453,11 @@ static void parseFloat3(float *x, float *y, float *z, const char **token) {
(*z) = parseFloat(token); (*z) = parseFloat(token);
} }
static unsigned int my_strnlen(const char *s, unsigned int n) {
const char *p = memchr(s, 0, n);
return p ? (unsigned int)(p - s) : n;
}
static char *my_strdup(const char *s, unsigned int max_length) { static char *my_strdup(const char *s, unsigned int max_length) {
char *d; char *d;
unsigned int len; unsigned int len;
@ -478,15 +483,13 @@ static char *my_strndup(const char *s, unsigned int len) {
if (s == NULL) return NULL; if (s == NULL) return NULL;
if (len == 0) return NULL; if (len == 0) return NULL;
d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ slen = my_strnlen(s, len);
slen = strlen(s); d = (char *)TINYOBJ_MALLOC(slen + 1); /* + '\0' */
if (slen < len) { if (!d) {
memcpy(d, s, slen); return NULL;
d[slen] = '\0';
} else {
memcpy(d, s, len);
d[len] = '\0';
} }
memcpy(d, s, slen);
d[slen] = '\0';
return d; return d;
} }

View File

@ -296,7 +296,7 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
float x = position.x; float x = position.x;
float y = position.y; float y = position.y;
float z = position.z; float z = position.z;
if (rlCheckBufferLimit(36)) rlglDraw(); if (rlCheckBufferLimit(36)) rlglDraw();
rlEnableTexture(texture.id); rlEnableTexture(texture.id);
@ -362,7 +362,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
{ {
int numVertex = (rings + 2)*slices*6; int numVertex = (rings + 2)*slices*6;
if (rlCheckBufferLimit(numVertex)) rlglDraw(); if (rlCheckBufferLimit(numVertex)) rlglDraw();
rlPushMatrix(); rlPushMatrix();
// NOTE: Transformation is applied in inverse order (scale -> translate) // NOTE: Transformation is applied in inverse order (scale -> translate)
rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
@ -405,7 +405,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
{ {
int numVertex = (rings + 2)*slices*6; int numVertex = (rings + 2)*slices*6;
if (rlCheckBufferLimit(numVertex)) rlglDraw(); if (rlCheckBufferLimit(numVertex)) rlglDraw();
rlPushMatrix(); rlPushMatrix();
// NOTE: Transformation is applied in inverse order (scale -> translate) // NOTE: Transformation is applied in inverse order (scale -> translate)
rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
@ -449,7 +449,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
int numVertex = sides*6; int numVertex = sides*6;
if (rlCheckBufferLimit(numVertex)) rlglDraw(); if (rlCheckBufferLimit(numVertex)) rlglDraw();
@ -508,7 +508,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
int numVertex = sides*8; int numVertex = sides*8;
if (rlCheckBufferLimit(numVertex)) rlglDraw(); if (rlCheckBufferLimit(numVertex)) rlglDraw();
@ -540,7 +540,7 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
void DrawPlane(Vector3 centerPos, Vector2 size, Color color) void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
{ {
if (rlCheckBufferLimit(4)) rlglDraw(); if (rlCheckBufferLimit(4)) rlglDraw();
// NOTE: Plane is always created on XZ ground // NOTE: Plane is always created on XZ ground
rlPushMatrix(); rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
@ -669,7 +669,7 @@ Model LoadModel(const char *fileName)
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
model.materials[0] = LoadMaterialDefault(); model.materials[0] = LoadMaterialDefault();
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
} }
return model; return model;
@ -725,9 +725,9 @@ Mesh *LoadMeshes(const char *fileName, int *meshCount)
{ {
Mesh *meshes = NULL; Mesh *meshes = NULL;
int count = 0; int count = 0;
// TODO: Load meshes from file (OBJ, IQM, GLTF) // TODO: Load meshes from file (OBJ, IQM, GLTF)
*meshCount = count; *meshCount = count;
return meshes; return meshes;
} }
@ -800,7 +800,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
{ {
Material *materials = NULL; Material *materials = NULL;
unsigned int count = 0; unsigned int count = 0;
// TODO: Support IQM and GLTF for materials parsing // TODO: Support IQM and GLTF for materials parsing
#if defined(SUPPORT_FILEFORMAT_MTL) #if defined(SUPPORT_FILEFORMAT_MTL)
@ -853,7 +853,7 @@ void UnloadMaterial(Material material)
{ {
if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id);
} }
RL_FREE(material.maps); RL_FREE(material.maps);
} }
@ -908,7 +908,7 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount)
float framerate; float framerate;
unsigned int flags; unsigned int flags;
} IQMAnim; } IQMAnim;
FILE *iqmFile; FILE *iqmFile;
IQMHeader iqm; IQMHeader iqm;
@ -1076,7 +1076,7 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount)
RL_FREE(framedata); RL_FREE(framedata);
RL_FREE(poses); RL_FREE(poses);
RL_FREE(anim); RL_FREE(anim);
fclose(iqmFile); fclose(iqmFile);
return animations; return animations;
@ -1151,7 +1151,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
void UnloadModelAnimation(ModelAnimation anim) void UnloadModelAnimation(ModelAnimation anim)
{ {
for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]); for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
RL_FREE(anim.bones); RL_FREE(anim.bones);
RL_FREE(anim.framePoses); RL_FREE(anim.framePoses);
} }
@ -1161,7 +1161,7 @@ void UnloadModelAnimation(ModelAnimation anim)
bool IsModelAnimationValid(Model model, ModelAnimation anim) bool IsModelAnimationValid(Model model, ModelAnimation anim)
{ {
int result = true; int result = true;
if (model.boneCount != anim.boneCount) result = false; if (model.boneCount != anim.boneCount) result = false;
else else
{ {
@ -2336,7 +2336,7 @@ void MeshTangents(Mesh *mesh)
RL_FREE(tan1); RL_FREE(tan1);
RL_FREE(tan2); RL_FREE(tan2);
// Load a new tangent attributes buffer // Load a new tangent attributes buffer
mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false); mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
@ -2491,7 +2491,7 @@ void DrawBoundingBox(BoundingBox box, Color color)
bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB)
{ {
bool collision = false; bool collision = false;
// Simple way to check for collision, just checking distance between two points // Simple way to check for collision, just checking distance between two points
// Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
/* /*
@ -2503,10 +2503,10 @@ bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, floa
if (distance <= (radiusA + radiusB)) collision = true; if (distance <= (radiusA + radiusB)) collision = true;
*/ */
// Check for distances squared to avoid sqrtf() // Check for distances squared to avoid sqrtf()
if (Vector3DotProduct(Vector3Subtract(centerB, centerA), Vector3Subtract(centerB, centerA)) <= (radiusA + radiusB)*(radiusA + radiusB)) collision = true; if (Vector3DotProduct(Vector3Subtract(centerB, centerA), Vector3Subtract(centerB, centerA)) <= (radiusA + radiusB)*(radiusA + radiusB)) collision = true;
return collision; return collision;
} }
@ -2798,7 +2798,7 @@ static Model LoadOBJ(const char *fileName)
model.materialCount = materialCount; model.materialCount = materialCount;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
} }
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
/* /*
@ -2860,6 +2860,9 @@ static Model LoadOBJ(const char *fileName)
// Assign mesh material for current mesh // Assign mesh material for current mesh
model.meshMaterial[m] = attrib.material_ids[m]; model.meshMaterial[m] = attrib.material_ids[m];
// Set unfound materials to default
if (model.meshMaterial[m] == -1) model.meshMaterial[m] = 0;
} }
// Init model materials // Init model materials
@ -2897,7 +2900,7 @@ static Model LoadOBJ(const char *fileName)
*/ */
model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined
if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd
model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3];
model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; model.materials[m].maps[MAP_DIFFUSE].value = 0.0f;
@ -2918,6 +2921,8 @@ static Model LoadOBJ(const char *fileName)
tinyobj_attrib_free(&attrib); tinyobj_attrib_free(&attrib);
tinyobj_shapes_free(meshes, meshCount); tinyobj_shapes_free(meshes, meshCount);
tinyobj_materials_free(materials, materialCount); tinyobj_materials_free(materials, materialCount);
RL_FREE(data);
} }
// NOTE: At this point we have all model data loaded // NOTE: At this point we have all model data loaded
@ -2966,13 +2971,13 @@ static Model LoadIQM(const char *fileName)
typedef struct IQMTriangle { typedef struct IQMTriangle {
unsigned int vertex[3]; unsigned int vertex[3];
} IQMTriangle; } IQMTriangle;
typedef struct IQMJoint { typedef struct IQMJoint {
unsigned int name; unsigned int name;
int parent; int parent;
float translate[3], rotate[4], scale[3]; float translate[3], rotate[4], scale[3];
} IQMJoint; } IQMJoint;
typedef struct IQMVertexArray { typedef struct IQMVertexArray {
unsigned int type; unsigned int type;
unsigned int flags; unsigned int flags;
@ -3090,7 +3095,7 @@ static Model LoadIQM(const char *fileName)
// NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
} }
@ -3286,7 +3291,7 @@ static const unsigned char base64Table[] = {
static int GetSizeBase64(char *input) static int GetSizeBase64(char *input)
{ {
int size = 0; int size = 0;
for (int i = 0; input[4*i] != 0; i++) for (int i = 0; input[4*i] != 0; i++)
{ {
if (input[4*i + 3] == '=') if (input[4*i + 3] == '=')
@ -3296,7 +3301,7 @@ static int GetSizeBase64(char *input)
} }
else size += 3; else size += 3;
} }
return size; return size;
} }
@ -3423,25 +3428,29 @@ static Texture LoadTextureFromCGLTFTextureView(cgltf_texture_view* view, Color t
static Model LoadGLTF(const char *fileName) static Model LoadGLTF(const char *fileName)
{ {
/*********************************************************************************** /***********************************************************************************
Function implemented by Wilhem Barbier (@wbrbr) Function implemented by Wilhem Barbier (@wbrbr)
Features: Features:
- Supports .gltf and .glb files - Supports .gltf and .glb files
- Supports embedded (base64) or external textures - Supports embedded (base64) or external textures
- Loads the albedo/diffuse texture (other maps could be added) - Loads the albedo/diffuse texture (other maps could be added)
- Supports multiple mesh per model and multiple primitives per model - Supports multiple mesh per model and multiple primitives per model
Some restrictions (not exhaustive): Some restrictions (not exhaustive):
- Triangle-only meshes - Triangle-only meshes
- Not supported node hierarchies or transforms - Not supported node hierarchies or transforms
- Only loads the diffuse texture... but not too hard to support other maps (normal, roughness/metalness...) - Only loads the diffuse texture... but not too hard to support other maps (normal, roughness/metalness...)
- Only supports unsigned short indices (no byte/unsigned int) - Only supports unsigned short indices (no byte/unsigned int)
- Only supports float for texture coordinates (no byte/unsigned short) - Only supports float for texture coordinates (no byte/unsigned short)
*************************************************************************************/ *************************************************************************************/
<<<<<<< HEAD
#define LOAD_ACCESSOR(type, nbcomp, acc, dst) \ #define LOAD_ACCESSOR(type, nbcomp, acc, dst) \
=======
#define LOAD_ACCESSOR(type, nbcomp, acc, dst) \
>>>>>>> 55129d509fe0095344d635ac9d996abf4ca3b67d
{ \ { \
int n = 0; \ int n = 0; \
type* buf = (type*)acc->buffer_view->buffer->data+acc->buffer_view->offset/sizeof(type)+acc->offset/sizeof(type); \ type* buf = (type*)acc->buffer_view->buffer->data+acc->buffer_view->offset/sizeof(type)+acc->offset/sizeof(type); \
@ -3452,7 +3461,7 @@ static Model LoadGLTF(const char *fileName)
n += acc->stride/sizeof(type);\ n += acc->stride/sizeof(type);\
}\ }\
} }
Model model = { 0 }; Model model = { 0 };
// glTF file loading // glTF file loading
@ -3486,7 +3495,7 @@ static Model LoadGLTF(const char *fileName)
result = cgltf_load_buffers(&options, data, fileName); result = cgltf_load_buffers(&options, data, fileName);
int primitivesCount = 0; int primitivesCount = 0;
for (int i = 0; i < data->meshes_count; i++) primitivesCount += (int)data->meshes[i].primitives_count; for (int i = 0; i < data->meshes_count; i++) primitivesCount += (int)data->meshes[i].primitives_count;
// Process glTF data and map to model // Process glTF data and map to model
@ -3495,7 +3504,7 @@ static Model LoadGLTF(const char *fileName)
model.materialCount = data->materials_count + 1; model.materialCount = data->materials_count + 1;
model.materials = RL_MALLOC(model.materialCount*sizeof(Material)); model.materials = RL_MALLOC(model.materialCount*sizeof(Material));
model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int)); model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int));
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 each material
@ -3504,6 +3513,7 @@ static Model LoadGLTF(const char *fileName)
model.materials[i] = LoadMaterialDefault(); model.materials[i] = LoadMaterialDefault();
Color tint = (Color){ 1.0f, 1.0f, 1.0f, 1.0f }; Color tint = (Color){ 1.0f, 1.0f, 1.0f, 1.0f };
const char *texPath = GetDirectoryPath(fileName); const char *texPath = GetDirectoryPath(fileName);
<<<<<<< HEAD
//Ensure material follows raylibe support for PBR (metallic/roughness flow) //Ensure material follows raylibe support for PBR (metallic/roughness flow)
if (data->materials[i].has_pbr_metallic_roughness) { if (data->materials[i].has_pbr_metallic_roughness) {
@ -3512,10 +3522,16 @@ static Model LoadGLTF(const char *fileName)
strcpy(model.materials[i].name, data->materials[i].name); strcpy(model.materials[i].name, data->materials[i].name);
=======
if (data->materials[i].has_pbr_metallic_roughness)
{
>>>>>>> 55129d509fe0095344d635ac9d996abf4ca3b67d
tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255.99f); tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255.99f);
tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255.99f); tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255.99f);
tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255.99f); 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); tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255.99f);
<<<<<<< HEAD
model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCGLTFTextureView(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, tint, texPath); model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCGLTFTextureView(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, tint, texPath);
@ -3526,13 +3542,103 @@ static Model LoadGLTF(const char *fileName)
model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCGLTFTextureView(data->materials[i].normal_texture.texture->image, tint, texPath); model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCGLTFTextureView(data->materials[i].normal_texture.texture->image, tint, texPath);
model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCGLTFTextureView(data->materials[i].occlusion_texture.texture->image, tint, texPath); model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCGLTFTextureView(data->materials[i].occlusion_texture.texture->image, tint, texPath);
=======
}
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)
{
cgltf_image *img = data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image;
if (img->uri)
{
if ((strlen(img->uri) > 5) &&
(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();
model.materials[i].maps[MAP_DIFFUSE].texture = texture;
>>>>>>> 55129d509fe0095344d635ac9d996abf4ca3b67d
} }
} }
model.materials[model.materialCount - 1] = LoadMaterialDefault(); model.materials[model.materialCount - 1] = LoadMaterialDefault();
int primitiveIndex = 0; int primitiveIndex = 0;
for (int i = 0; i < data->meshes_count; i++) for (int i = 0; i < data->meshes_count; i++)
{ {
for (int p = 0; p < data->meshes[i].primitives_count; p++) for (int p = 0; p < data->meshes[i].primitives_count; p++)
@ -3557,7 +3663,7 @@ static Model LoadGLTF(const char *fileName)
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord) else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
{ {
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data; cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
if (acc->component_type == cgltf_component_type_r_32f) if (acc->component_type == cgltf_component_type_r_32f)
{ {
model.meshes[primitiveIndex].texcoords = RL_MALLOC(sizeof(float)*acc->count*2); model.meshes[primitiveIndex].texcoords = RL_MALLOC(sizeof(float)*acc->count*2);
@ -3565,14 +3671,14 @@ static Model LoadGLTF(const char *fileName)
} }
else else
{ {
// TODO: support normalized unsigned byte/unsigned short texture coordinates // TODO: Support normalized unsigned byte/unsigned short texture coordinates
TraceLog(LOG_WARNING, "[%s] Texture coordinates must be float", fileName); TraceLog(LOG_WARNING, "[%s] Texture coordinates must be float", fileName);
} }
} }
} }
cgltf_accessor *acc = data->meshes[i].primitives[p].indices; cgltf_accessor *acc = data->meshes[i].primitives[p].indices;
if (acc) if (acc)
{ {
if (acc->component_type == cgltf_component_type_r_16u) if (acc->component_type == cgltf_component_type_r_16u)
@ -3583,7 +3689,7 @@ static Model LoadGLTF(const char *fileName)
} }
else else
{ {
// TODO: support unsigned byte/unsigned int // TODO: Support unsigned byte/unsigned int
TraceLog(LOG_WARNING, "[%s] Indices must be unsigned short", fileName); TraceLog(LOG_WARNING, "[%s] Indices must be unsigned short", fileName);
} }
} }
@ -3602,7 +3708,7 @@ static Model LoadGLTF(const char *fileName)
{ {
model.meshMaterial[primitiveIndex] = model.materialCount - 1;; model.meshMaterial[primitiveIndex] = model.materialCount - 1;;
} }
primitiveIndex++; primitiveIndex++;
} }
} }

View File

@ -124,7 +124,7 @@
// After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a
// standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough
// In case of music-stalls, just increase this number // In case of music-stalls, just increase this number
#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) #define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb)
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
@ -189,6 +189,8 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo
#define DEVICE_CHANNELS 2 #define DEVICE_CHANNELS 2
#define DEVICE_SAMPLE_RATE 44100 #define DEVICE_SAMPLE_RATE 44100
#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16
typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage;
// Audio buffer structure // Audio buffer structure
@ -205,16 +207,22 @@ struct rAudioBuffer {
bool looping; // Audio buffer looping, always true for AudioStreams bool looping; // Audio buffer looping, always true for AudioStreams
int usage; // Audio buffer usage mode: STATIC or STREAM int usage; // Audio buffer usage mode: STATIC or STREAM
bool isSubBufferProcessed[2]; bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
unsigned int frameCursorPos; // Samples processed? unsigned int frameCursorPos; // Frame cursor position
unsigned int bufferSizeInFrames; unsigned int bufferSizeInFrames; // Total buffer size in frames
unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming)
rAudioBuffer *next; unsigned char *buffer; // Data buffer, on music stream keeps filling
rAudioBuffer *prev;
unsigned char *buffer; rAudioBuffer *next; // Next audio buffer on the list
rAudioBuffer *prev; // Previous audio buffer on the list
}; };
#define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision #define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision
// Audio buffers are tracked in a linked list
static AudioBuffer *firstAudioBuffer = NULL;
static AudioBuffer *lastAudioBuffer = NULL;
// miniaudio global variables // miniaudio global variables
static ma_context context; static ma_context context;
@ -223,9 +231,10 @@ static ma_mutex audioLock;
static bool isAudioInitialized = false; static bool isAudioInitialized = false;
static float masterVolume = 1.0f; static float masterVolume = 1.0f;
// Audio buffers are tracked in a linked list // Multi channel playback global variables
static AudioBuffer *firstAudioBuffer = NULL; AudioBuffer *audioBufferPool[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 };
static AudioBuffer *lastAudioBuffer = NULL; unsigned int audioBufferPoolCounter = 0;
unsigned int audioBufferPoolChannels[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 };
// miniaudio functions declaration // miniaudio functions declaration
static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message); static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
@ -247,19 +256,9 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
void TrackAudioBuffer(AudioBuffer *buffer); void TrackAudioBuffer(AudioBuffer *buffer);
void UntrackAudioBuffer(AudioBuffer *buffer); void UntrackAudioBuffer(AudioBuffer *buffer);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Multi channel playback globals // miniaudio functions definitions
//----------------------------------------------------------------------------------
// Number of channels in the audio pool
#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16
// Audio buffer pool
AudioBuffer *audioBufferPool[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 };
// These are used to determine the oldest playing channel
unsigned long audioBufferPoolCounter = 0;
unsigned long audioBufferPoolChannels[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 };
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Log callback function // Log callback function
@ -319,6 +318,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
{ {
float *framesOut = (float *)pFramesOut + (framesRead*device.playback.channels); float *framesOut = (float *)pFramesOut + (framesRead*device.playback.channels);
float *framesIn = tempBuffer; float *framesIn = tempBuffer;
MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume);
framesToRead -= framesJustRead; framesToRead -= framesJustRead;
@ -485,7 +485,6 @@ 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);
@ -553,11 +552,7 @@ void InitAudioDevice(void)
// Close the audio device for all contexts // Close the audio device for all contexts
void CloseAudioDevice(void) void CloseAudioDevice(void)
{ {
if (!isAudioInitialized) if (isAudioInitialized)
{
TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
}
else
{ {
ma_mutex_uninit(&audioLock); ma_mutex_uninit(&audioLock);
ma_device_uninit(&device); ma_device_uninit(&device);
@ -567,6 +562,7 @@ void CloseAudioDevice(void)
TraceLog(LOG_INFO, "Audio device closed successfully"); TraceLog(LOG_INFO, "Audio device closed successfully");
} }
else TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
} }
// Check if device has been initialized successfully // Check if device has been initialized successfully
@ -588,11 +584,11 @@ void SetMasterVolume(float volume)
// Module Functions Definition - Audio Buffer management // Module Functions Definition - Audio Buffer management
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Create a new audio buffer. Initially filled with silence // Initialize a new audio buffer (filled with silence)
AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage) AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage)
{ {
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)
{ {
@ -690,6 +686,7 @@ void StopAudioBuffer(AudioBuffer *buffer)
buffer->playing = false; buffer->playing = false;
buffer->paused = false; buffer->paused = false;
buffer->frameCursorPos = 0; buffer->frameCursorPos = 0;
buffer->totalFramesProcessed = 0;
buffer->isSubBufferProcessed[0] = true; buffer->isSubBufferProcessed[0] = true;
buffer->isSubBufferProcessed[1] = true; buffer->isSubBufferProcessed[1] = true;
} }
@ -725,8 +722,10 @@ 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. Note that this changes the duration of the sound - higher pitches // Pitching is just an adjustment of the sample rate.
// will make the sound faster; lower pitches make it slower. // Note that this changes the duration of the sound:
// - higher pitches will make the sound faster
// - 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);
buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate; buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate;
@ -869,16 +868,14 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
{ {
AudioBuffer *audioBuffer = sound.stream.buffer; AudioBuffer *audioBuffer = sound.stream.buffer;
if (audioBuffer == NULL) if (audioBuffer != NULL)
{ {
TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); StopAudioBuffer(audioBuffer);
return;
// TODO: May want to lock/unlock this since this data buffer is read at mixing time
memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
} }
else TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
StopAudioBuffer(audioBuffer);
// TODO: May want to lock/unlock this since this data buffer is read at mixing time
memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
} }
// Export wave data to file // Export wave data to file
@ -912,38 +909,41 @@ 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");
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); if (txtFile != NULL)
fprintf(txtFile, "// //\n"); {
fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n"); fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n");
fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n");
fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n"); fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
fprintf(txtFile, "// //\n");
fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n");
#if !defined(RAUDIO_STANDALONE) #if !defined(RAUDIO_STANDALONE)
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
#else #else
strcpy(varFileName, fileName); strcpy(varFileName, fileName);
#endif #endif
fprintf(txtFile, "// Wave data information\n"); fprintf(txtFile, "// Wave data information\n");
fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount); fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount);
fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate); fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate);
fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize); fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize);
fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels); fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels);
// Write byte data as hexadecimal text // Write byte data as hexadecimal text
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]); for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]);
fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]); fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]);
fclose(txtFile); fclose(txtFile);
}
} }
// Play a sound // Play a sound
@ -956,7 +956,7 @@ void PlaySound(Sound sound)
void PlaySoundMulti(Sound sound) void PlaySoundMulti(Sound sound)
{ {
int index = -1; int index = -1;
unsigned long oldAge = 0; unsigned int oldAge = 0;
int oldIndex = -1; int oldIndex = -1;
// find the first non playing pool entry // find the first non playing pool entry
@ -1184,14 +1184,8 @@ Music LoadMusicStream(const char *fileName)
// OGG bit rate defaults to 16 bit, it's enough for compressed format // OGG bit rate defaults to 16 bit, it's enough for compressed format
music.stream = InitAudioStream(info.sample_rate, 16, info.channels); music.stream = InitAudioStream(info.sample_rate, 16, info.channels);
music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels; music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels;
music.sampleLeft = music.sampleCount;
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
TraceLog(LOG_INFO, "[%s] OGG total samples: %i", fileName, music.sampleCount);
TraceLog(LOG_INFO, "[%s] OGG sample rate: %i", fileName, info.sample_rate);
TraceLog(LOG_INFO, "[%s] OGG channels: %i", fileName, info.channels);
TraceLog(LOG_INFO, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
} }
} }
#endif #endif
@ -1207,14 +1201,8 @@ Music LoadMusicStream(const char *fileName)
music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
music.sampleCount = (unsigned int)ctxFlac->totalSampleCount; music.sampleCount = (unsigned int)ctxFlac->totalSampleCount;
music.sampleLeft = music.sampleCount;
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music.sampleCount);
TraceLog(LOG_DEBUG, "[%s] FLAC sample rate: %i", fileName, ctxFlac->sampleRate);
TraceLog(LOG_DEBUG, "[%s] FLAC bits per sample: %i", fileName, ctxFlac->bitsPerSample);
TraceLog(LOG_DEBUG, "[%s] FLAC channels: %i", fileName, ctxFlac->channels);
} }
} }
#endif #endif
@ -1232,14 +1220,8 @@ Music LoadMusicStream(const char *fileName)
music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels; music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
music.sampleLeft = music.sampleCount;
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, ctxMp3->sampleRate);
TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32);
TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, ctxMp3->channels);
TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music.sampleCount);
} }
} }
#endif #endif
@ -1258,14 +1240,10 @@ Music LoadMusicStream(const char *fileName)
// NOTE: Only stereo is supported for XM // NOTE: Only stereo is supported for XM
music.stream = InitAudioStream(48000, 16, 2); music.stream = InitAudioStream(48000, 16, 2);
music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm);
music.sampleLeft = music.sampleCount;
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
music.ctxData = ctxXm; music.ctxData = ctxXm;
TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music.sampleCount);
TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f);
} }
} }
#endif #endif
@ -1285,12 +1263,8 @@ Music LoadMusicStream(const char *fileName)
// NOTE: Only stereo is supported for MOD // NOTE: Only stereo is supported for MOD
music.stream = InitAudioStream(48000, 16, 2); music.stream = InitAudioStream(48000, 16, 2);
music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod); music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod);
music.sampleLeft = music.sampleCount;
music.loopCount = 0; // Infinite loop by default music.loopCount = 0; // Infinite loop by default
musicLoaded = true; musicLoaded = true;
TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music.sampleCount);
TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f);
} }
} }
#endif #endif
@ -1316,6 +1290,15 @@ Music LoadMusicStream(const char *fileName)
TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName); TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName);
} }
else
{
// Show some music stream info
TraceLog(LOG_INFO, "[%s] Music file successfully loaded:", fileName);
TraceLog(LOG_INFO, " Total samples: %i", music.sampleCount);
TraceLog(LOG_INFO, " Sample rate: %i Hz", music.stream.sampleRate);
TraceLog(LOG_INFO, " Sample size: %i bits", music.stream.sampleSize);
TraceLog(LOG_INFO, " Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi");
}
return music; return music;
} }
@ -1348,21 +1331,18 @@ void PlayMusicStream(Music music)
{ {
AudioBuffer *audioBuffer = music.stream.buffer; AudioBuffer *audioBuffer = music.stream.buffer;
if (audioBuffer == NULL) if (audioBuffer != NULL)
{ {
TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); // For music streams, we need to make sure we maintain the frame cursor position
return; // 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
// play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music);
ma_uint32 frameCursorPos = audioBuffer->frameCursorPos;
PlayAudioStream(music.stream); // WARNING: This resets the cursor position.
audioBuffer->frameCursorPos = frameCursorPos;
} }
else TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer");
// 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()
// NOTE: In case window is minimized, music stream is stopped, just make sure to
// play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music);
ma_uint32 frameCursorPos = audioBuffer->frameCursorPos;
PlayAudioStream(music.stream); // <-- This resets the cursor position.
audioBuffer->frameCursorPos = frameCursorPos;
} }
// Pause music playing // Pause music playing
@ -1389,7 +1369,7 @@ void StopMusicStream(Music music)
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, 0); break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MP3) #if defined(SUPPORT_FILEFORMAT_MP3)
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break; case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break;
@ -1402,8 +1382,6 @@ void StopMusicStream(Music music)
#endif #endif
default: break; default: break;
} }
music.sampleLeft = music.sampleCount;
} }
// Update (re-fill) music buffers if data already processed // Update (re-fill) music buffers if data already processed
@ -1416,12 +1394,16 @@ void UpdateMusicStream(Music music)
// NOTE: Using dynamic allocation because it could require more than 16KB // NOTE: Using dynamic allocation because it could require more than 16KB
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 steamed 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...
//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);
while (IsAudioStreamProcessed(music.stream)) while (IsAudioStreamProcessed(music.stream))
{ {
if ((music.sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels; if ((sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels;
else samplesCount = music.sampleLeft; else samplesCount = sampleLeft;
switch (music.ctxType) switch (music.ctxType)
{ {
@ -1437,7 +1419,7 @@ void UpdateMusicStream(Music music)
case MUSIC_AUDIO_FLAC: case MUSIC_AUDIO_FLAC:
{ {
// NOTE: Returns the number of samples to process (not required) // NOTE: Returns the number of samples to process (not required)
drflac_read_s16((drflac *)music.ctxData, samplesCount, (short *)pcm); drflac_read_pcm_frames_s16((drflac *)music.ctxData, samplesCount, (short *)pcm);
} break; } break;
#endif #endif
@ -1470,12 +1452,12 @@ void UpdateMusicStream(Music music)
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) music.sampleLeft -= samplesCount/2; if (samplesCount > 1) sampleLeft -= samplesCount/2;
else music.sampleLeft -= samplesCount; else sampleLeft -= samplesCount;
} }
else music.sampleLeft -= samplesCount; else sampleLeft -= samplesCount;
if (music.sampleLeft <= 0) if (sampleLeft <= 0)
{ {
streamEnding = true; streamEnding = true;
break; break;
@ -1493,13 +1475,10 @@ void UpdateMusicStream(Music music)
// Decrease loopCount to stop when required // Decrease loopCount to stop when required
if (music.loopCount > 1) if (music.loopCount > 1)
{ {
music.loopCount--; // Decrease loop count music.loopCount--; // Decrease loop count
PlayMusicStream(music); // Play again PlayMusicStream(music); // Play again
} }
else else if (music.loopCount == 0) PlayMusicStream(music);
{
if (music.loopCount == 0) PlayMusicStream(music);
}
} }
else else
{ {
@ -1549,7 +1528,8 @@ float GetMusicTimePlayed(Music music)
{ {
float secondsPlayed = 0.0f; float secondsPlayed = 0.0f;
unsigned int samplesPlayed = music.sampleCount - music.sampleLeft; //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels;
unsigned int samplesPlayed = music.stream.buffer->totalFramesProcessed*music.stream.channels;
secondsPlayed = (float)samplesPlayed/(music.stream.sampleRate*music.stream.channels); secondsPlayed = (float)samplesPlayed/(music.stream.sampleRate*music.stream.channels);
return secondsPlayed; return secondsPlayed;
@ -1562,14 +1542,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
stream.sampleRate = sampleRate; stream.sampleRate = sampleRate;
stream.sampleSize = sampleSize; stream.sampleSize = sampleSize;
stream.channels = channels;
// Only mono and stereo channels are supported
if ((channels > 0) && (channels < 3)) stream.channels = channels;
else
{
TraceLog(LOG_WARNING, "Init audio stream: Number of channels not supported: %i", channels);
stream.channels = 1; // Fallback to mono channel
}
ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32)); ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32));
@ -1579,18 +1552,14 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
if (subBufferSize < periodSize) subBufferSize = periodSize; if (subBufferSize < periodSize) subBufferSize = periodSize;
AudioBuffer *audioBuffer = 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 (audioBuffer == NULL) if (stream.buffer != NULL)
{ {
TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); stream.buffer->looping = true; // Always loop for streaming buffers
return stream; TraceLog(LOG_INFO, "Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo");
} }
else TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer");
audioBuffer->looping = true; // Always loop for streaming buffers
stream.buffer = audioBuffer;
TraceLog(LOG_INFO, "Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo");
return stream; return stream;
} }
@ -1610,56 +1579,54 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
{ {
AudioBuffer *audioBuffer = stream.buffer; AudioBuffer *audioBuffer = stream.buffer;
if (audioBuffer == NULL) if (audioBuffer != NULL)
{ {
TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
return;
}
if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
{
ma_uint32 subBufferToUpdate = 0;
if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1])
{ {
// Both buffers are available for updating. ma_uint32 subBufferToUpdate = 0;
// Update the first one and make sure the cursor is moved back to the front.
subBufferToUpdate = 0;
audioBuffer->frameCursorPos = 0;
}
else
{
// Just update whichever sub-buffer is processed.
subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1;
}
ma_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1])
unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
// Does this API expect a whole buffer to be updated in one go?
// Assuming so, but if not will need to change this logic.
if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels)
{
ma_uint32 framesToWrite = subBufferSizeInFrames;
if (framesToWrite > ((ma_uint32)samplesCount/stream.channels)) framesToWrite = (ma_uint32)samplesCount/stream.channels;
ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
memcpy(subBuffer, data, bytesToWrite);
// Any leftover frames should be filled with zeros.
ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
if (leftoverFrameCount > 0)
{ {
memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); // Both buffers are available for updating.
// Update the first one and make sure the cursor is moved back to the front.
subBufferToUpdate = 0;
audioBuffer->frameCursorPos = 0;
}
else
{
// Just update whichever sub-buffer is processed.
subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1;
} }
audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; ma_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
// TODO: Get total frames processed on this buffer... DOES NOT WORK.
audioBuffer->totalFramesProcessed += subBufferSizeInFrames;
// Does this API expect a whole buffer to be updated in one go?
// Assuming so, but if not will need to change this logic.
if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels)
{
ma_uint32 framesToWrite = subBufferSizeInFrames;
if (framesToWrite > ((ma_uint32)samplesCount/stream.channels)) framesToWrite = (ma_uint32)samplesCount/stream.channels;
ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
memcpy(subBuffer, data, bytesToWrite);
// Any leftover frames should be filled with zeros.
ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
if (leftoverFrameCount > 0) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8));
audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false;
}
else TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer");
} }
else TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer"); else TraceLog(LOG_ERROR, "UpdateAudioStream() : Audio buffer not available for updating");
} }
else TraceLog(LOG_ERROR, "Audio buffer not available for updating"); else TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer");
} }
// Check if any audio stream buffers requires refill // Check if any audio stream buffers requires refill
@ -1964,7 +1931,7 @@ static Wave LoadFLAC(const char *fileName)
// Decode an entire FLAC file in one go // Decode an entire FLAC file in one go
uint64_t totalSampleCount; uint64_t totalSampleCount;
wave.data = drflac_open_and_decode_file_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); wave.data = drflac_open_file_and_read_pcm_frames_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount);
wave.sampleCount = (unsigned int)totalSampleCount; wave.sampleCount = (unsigned int)totalSampleCount;
wave.sampleSize = 16; wave.sampleSize = 16;

View File

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raudio - A simple and easy-to-use audio library based on mini_al * raudio - A simple and easy-to-use audio library based on miniaudio
* *
* FEATURES: * FEATURES:
* - Manage audio device (init/close) * - Manage audio device (init/close)
@ -20,7 +20,7 @@
* *
* CONTRIBUTORS: * CONTRIBUTORS:
* David Reid (github: @mackron) (Nov. 2017): * David Reid (github: @mackron) (Nov. 2017):
* - Complete port to mini_al library * - Complete port to miniaudio library
* *
* Joshua Reisenauer (github: @kd7tck) (2015) * Joshua Reisenauer (github: @kd7tck) (2015)
* - XM audio module support (jar_xm) * - XM audio module support (jar_xm)
@ -112,7 +112,6 @@ typedef struct Music {
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 sampleLeft; // Number of samples left to end
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
AudioStream stream; // Audio stream AudioStream stream; // Audio stream

View File

@ -435,7 +435,6 @@ typedef struct Music {
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 sampleLeft; // Number of samples left to end
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
AudioStream stream; // Audio stream AudioStream stream; // Audio stream
@ -884,6 +883,7 @@ RLAPI int GetMonitorWidth(int monitor); // Get primary
RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height
RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres
RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres
RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor
RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor
RLAPI const char *GetClipboardText(void); // Get clipboard text content RLAPI const char *GetClipboardText(void); // Get clipboard text content
RLAPI void SetClipboardText(const char *text); // Set clipboard text content RLAPI void SetClipboardText(const char *text); // Set clipboard text content
@ -957,6 +957,9 @@ RLAPI char **GetDroppedFiles(int *count); // Get dropped
RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory)
RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
RLAPI unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength); // Compress data (DEFLATE algorythm)
RLAPI unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength); // Decompress data (DEFLATE algorythm)
// Persistent storage management // Persistent storage management
RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position)
RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position) RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position)
@ -1302,7 +1305,7 @@ RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight);
// Shader loading/unloading functions // Shader loading/unloading functions
RLAPI char *LoadText(const char *fileName); // Load chars array from text file RLAPI char *LoadText(const char *fileName); // Load chars array from text file
RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations
RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations RLAPI Shader LoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations
RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM)
RLAPI Shader GetShaderDefault(void); // Get default shader RLAPI Shader GetShaderDefault(void); // Get default shader

View File

@ -519,7 +519,7 @@ RLAPI void rlUnloadMesh(Mesh mesh); // Unl
// Shader loading/unloading functions // Shader loading/unloading functions
RLAPI char *LoadText(const char *fileName); // Load chars array from text file RLAPI char *LoadText(const char *fileName); // Load chars array from text file
RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations
RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations RLAPI Shader LoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations
RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM)
RLAPI Shader GetShaderDefault(void); // Get default shader RLAPI Shader GetShaderDefault(void); // Get default shader
@ -1527,7 +1527,7 @@ void rlglInit(int width, int height)
// Allocate numExt strings pointers // Allocate numExt strings pointers
const char **extList = RL_MALLOC(sizeof(const char *)*numExt); const char **extList = RL_MALLOC(sizeof(const char *)*numExt);
// Get extensions strings // Get extensions strings
for (int i = 0; i < numExt; i++) extList[i] = (const char *)glGetStringi(GL_EXTENSIONS, i); for (int i = 0; i < numExt; i++) extList[i] = (const char *)glGetStringi(GL_EXTENSIONS, i);
@ -1541,7 +1541,7 @@ void rlglInit(int width, int height)
int len = strlen(extensions) + 1; int len = strlen(extensions) + 1;
char *extensionsDup = (char *)RL_CALLOC(len, sizeof(char)); char *extensionsDup = (char *)RL_CALLOC(len, sizeof(char));
strcpy(extensionsDup, extensions); strcpy(extensionsDup, extensions);
extList[numExt] = extensionsDup; extList[numExt] = extensionsDup;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
@ -1549,13 +1549,13 @@ void rlglInit(int width, int height)
if (extensionsDup[i] == ' ') if (extensionsDup[i] == ' ')
{ {
extensionsDup[i] = '\0'; extensionsDup[i] = '\0';
numExt++; numExt++;
extList[numExt] = &extensionsDup[i + 1]; extList[numExt] = &extensionsDup[i + 1];
} }
} }
// NOTE: Duplicated string (extensionsDup) must be deallocated // NOTE: Duplicated string (extensionsDup) must be deallocated
#endif #endif
TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt); TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt);
@ -2636,11 +2636,11 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform)
// That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix()
Matrix matView = modelview; // View matrix (camera) Matrix matView = modelview; // View matrix (camera)
Matrix matProjection = projection; // Projection matrix (perspective) Matrix matProjection = projection; // Projection matrix (perspective)
// TODO: Matrix nightmare! Trying to combine stack matrices with view matrix and local model transform matrix.. // TODO: Matrix nightmare! Trying to combine stack matrices with view matrix and local model transform matrix..
// There is some problem in the order matrices are multiplied... it requires some time to figure out... // There is some problem in the order matrices are multiplied... it requires some time to figure out...
Matrix matStackTransform = MatrixIdentity(); Matrix matStackTransform = MatrixIdentity();
// TODO: Consider possible transform matrices in the stack // TODO: Consider possible transform matrices in the stack
// Is this the right order? or should we start with the first stored matrix instead of the last one? // Is this the right order? or should we start with the first stored matrix instead of the last one?
//for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform); //for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform);
@ -2967,7 +2967,8 @@ 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 };
shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int));
// NOTE: Shader.locs is allocated by LoadShaderCode()
char *vShaderStr = NULL; char *vShaderStr = NULL;
char *fShaderStr = NULL; char *fShaderStr = NULL;
@ -2985,7 +2986,7 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName)
// Load shader from code strings // Load shader from code strings
// NOTE: If shader string is NULL, using default vertex/fragment shaders // NOTE: If shader string is NULL, using default vertex/fragment shaders
Shader LoadShaderCode(char *vsCode, char *fsCode) Shader LoadShaderCode(const char *vsCode, const char *fsCode)
{ {
Shader shader = { 0 }; Shader shader = { 0 };
shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int));
@ -3054,7 +3055,7 @@ void UnloadShader(Shader shader)
rlDeleteShader(shader.id); rlDeleteShader(shader.id);
TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id);
} }
RL_FREE(shader.locs); RL_FREE(shader.locs);
} }
@ -3867,7 +3868,7 @@ static Shader LoadShaderDefault(void)
for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
// Vertex shader directly defined, no external file required // Vertex shader directly defined, no external file required
char defaultVShaderStr[] = const char *defaultVShaderStr =
#if defined(GRAPHICS_API_OPENGL_21) #if defined(GRAPHICS_API_OPENGL_21)
"#version 120 \n" "#version 120 \n"
#elif defined(GRAPHICS_API_OPENGL_ES2) #elif defined(GRAPHICS_API_OPENGL_ES2)
@ -3896,7 +3897,7 @@ static Shader LoadShaderDefault(void)
"} \n"; "} \n";
// Fragment shader directly defined, no external file required // Fragment shader directly defined, no external file required
char defaultFShaderStr[] = const char *defaultFShaderStr =
#if defined(GRAPHICS_API_OPENGL_21) #if defined(GRAPHICS_API_OPENGL_21)
"#version 120 \n" "#version 120 \n"
#elif defined(GRAPHICS_API_OPENGL_ES2) #elif defined(GRAPHICS_API_OPENGL_ES2)
@ -4615,8 +4616,8 @@ int GetPixelDataSize(int width, int height, int format)
} }
dataSize = width*height*bpp/8; // Total data size in bytes dataSize = width*height*bpp/8; // Total data size in bytes
// Most compressed formats works on 4x4 blocks, // Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16 // if texture is smaller, minimum dataSize is 8 or 16
if ((width < 4) && (height < 4)) if ((width < 4) && (height < 4))
{ {

View File

@ -1183,7 +1183,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int
void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{ {
if (rlCheckBufferLimit(4)) rlglDraw(); if (rlCheckBufferLimit(4)) rlglDraw();
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
@ -1219,7 +1219,7 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{ {
if (rlCheckBufferLimit(6)) rlglDraw(); if (rlCheckBufferLimit(6)) rlglDraw();
rlBegin(RL_LINES); rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(v1.x, v1.y); rlVertex2f(v1.x, v1.y);
@ -1298,6 +1298,7 @@ void DrawTriangleStrip(Vector2 *points, int pointsCount, Color color)
void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
float centralAngle = 0.0f;
if (rlCheckBufferLimit(4*(360/sides))) rlglDraw(); if (rlCheckBufferLimit(4*(360/sides))) rlglDraw();
@ -1309,7 +1310,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 360/sides) for (int i = 0; i < sides; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
@ -1317,25 +1318,28 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlVertex2f(0, 0); rlVertex2f(0, 0);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
centralAngle += 360.0f/(float)sides;
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius); rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
} }
rlEnd(); rlEnd();
rlDisableTexture(); rlDisableTexture();
#else #else
rlBegin(RL_TRIANGLES); rlBegin(RL_TRIANGLES);
for (int i = 0; i < 360; i += 360/sides) for (int i = 0; i < sides; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(0, 0); rlVertex2f(0, 0);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
centralAngle += 360.0f/(float)sides;
rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
} }
rlEnd(); rlEnd();
#endif #endif
@ -1511,9 +1515,9 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
static float EaseCubicInOut(float t, float b, float c, float d) static float EaseCubicInOut(float t, float b, float c, float d)
{ {
if ((t /= 0.5f*d) < 1) return 0.5f*c*t*t*t + b; if ((t /= 0.5f*d) < 1) return 0.5f*c*t*t*t + b;
t -= 2; t -= 2;
return 0.5f*c*(t*t*t + 2.0f) + b; return 0.5f*c*(t*t*t + 2.0f) + b;
} }

View File

@ -161,6 +161,9 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_GIF)
static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays); // Load animated GIF file
#endif
#if defined(SUPPORT_FILEFORMAT_DDS) #if defined(SUPPORT_FILEFORMAT_DDS)
static Image LoadDDS(const char *fileName); // Load DDS file static Image LoadDDS(const char *fileName); // Load DDS file
#endif #endif
@ -253,13 +256,10 @@ Image LoadImage(const char *fileName)
FILE *imFile = fopen(fileName, "rb"); FILE *imFile = fopen(fileName, "rb");
stbi_set_flip_vertically_on_load(true);
// Load 32 bit per channel floats data // Load 32 bit per channel floats data
//stbi_set_flip_vertically_on_load(true);
image.data = stbi_loadf_from_file(imFile, &image.width, &image.height, &imgBpp, 0); image.data = stbi_loadf_from_file(imFile, &image.width, &image.height, &imgBpp, 0);
stbi_set_flip_vertically_on_load(false);
fclose(imFile); fclose(imFile);
image.mipmaps = 1; image.mipmaps = 1;
@ -551,7 +551,7 @@ Color *GetImageData(Image image)
pixels[i].a = 255; pixels[i].a = 255;
k += 3; k += 3;
} } break;
case UNCOMPRESSED_R32G32B32A32: case UNCOMPRESSED_R32G32B32A32:
{ {
pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
@ -560,7 +560,7 @@ Color *GetImageData(Image image)
pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f); pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
k += 4; k += 4;
} } break;
default: break; default: break;
} }
} }
@ -849,38 +849,40 @@ void ExportImageAsCode(Image image, const char *fileName)
{ {
#define BYTES_TEXT_PER_LINE 20 #define BYTES_TEXT_PER_LINE 20
char varFileName[256] = { 0 };
int dataSize = GetPixelDataSize(image.width, image.height, image.format);
FILE *txtFile = fopen(fileName, "wt"); FILE *txtFile = fopen(fileName, "wt");
if (txtFile != NULL)
{
char varFileName[256] = { 0 };
int dataSize = GetPixelDataSize(image.width, image.height, image.format);
fprintf(txtFile, "\n"); fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n");
fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n");
fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n");
fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n");
fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// Copyright (c) 2019 Ramon Santamaria (@raysan5) //\n");
fprintf(txtFile, "// Copyright (c) 2019 Ramon Santamaria (@raysan5) //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Add image information // Add image information
fprintf(txtFile, "// Image data information\n"); fprintf(txtFile, "// Image data information\n");
fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width); fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width);
fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height); fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height);
fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format); fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]); for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
fclose(txtFile); fclose(txtFile);
}
} }
// Copy an image to a new image // Copy an image to a new image
@ -1336,20 +1338,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 make sure cropping rectangle is inside margins // Security checks to validate crop rectangle
if ((crop.x + crop.width) > image->width) if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
{ if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
crop.width = image->width - crop.x; if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
TraceLog(LOG_WARNING, "Crop rectangle width out of bounds, rescaled crop width: %i", crop.width); 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;
TraceLog(LOG_WARNING, "Crop rectangle height out of bounds, rescaled crop height: %i", crop.height);
}
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
@ -1377,10 +1372,7 @@ void ImageCrop(Image *image, Rectangle crop)
// Reformat 32bit RGBA image to original format // Reformat 32bit RGBA image to original format
ImageFormat(image, format); ImageFormat(image, format);
} }
else else TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds");
{
TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds");
}
} }
// Crop image depending on alpha value // Crop image depending on alpha value
@ -1826,7 +1818,9 @@ 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
ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle
// Crop source image to desired source rectangle (if required)
if ((src.width != (int)srcRec.width) && (src.height != (int)srcRec.height)) ImageCrop(&srcCopy, srcRec);
// Scale source image in case destination rec size is different than source rec size // Scale source image in case destination rec size is different than source rec size
if (((int)dstRec.width != (int)srcRec.width) || ((int)dstRec.height != (int)srcRec.height)) if (((int)dstRec.width != (int)srcRec.width) || ((int)dstRec.height != (int)srcRec.height))
@ -1856,7 +1850,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
dstRec.y = 0; dstRec.y = 0;
} }
if (dstRec.y > (dst->height - dstRec.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 });
dstRec.height = dst->height - dstRec.y; dstRec.height = dst->height - dstRec.y;
@ -2961,6 +2955,45 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destR
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Definition // Module specific Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_GIF)
// Load animated GIF data
// - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
// - Number of frames is returned through 'frames' parameter
// - Frames delay is returned through 'delays' parameter (int array)
// - All frames are returned in RGBA format
static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays)
{
Image image = { 0 };
FILE *gifFile = fopen(fileName, "rb");
if (gifFile == NULL)
{
TraceLog(LOG_WARNING, "[%s] Animated GIF file could not be opened", fileName);
}
else
{
fseek(gifFile, 0L, SEEK_END);
int size = ftell(gifFile);
fseek(gifFile, 0L, SEEK_SET);
unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char));
fread(buffer, sizeof(char), size, gifFile);
fclose(gifFile); // Close file pointer
int comp = 0;
image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4);
image.mipmaps = 1;
image.format = UNCOMPRESSED_R8G8B8A8;
free(buffer);
}
return image;
}
#endif
#if defined(SUPPORT_FILEFORMAT_DDS) #if defined(SUPPORT_FILEFORMAT_DDS)
// Loading DDS image data (compressed or uncompressed) // Loading DDS image data (compressed or uncompressed)