From be6d237b9ebbe245de4384c17b84e75dab0f4981 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 20:22:50 +0100 Subject: Review models examples --- examples/models/models_cubicmap.c | 2 +- examples/models/models_heightmap.c | 2 +- examples/models/models_material_pbr.c | 14 +++++++------- examples/models/models_mesh_generation.c | 13 ++++++++++++- examples/models/models_mesh_picking.c | 6 +++--- examples/models/models_obj_loading.c | 2 +- examples/models/models_obj_viewer.c | 18 +++++++++--------- examples/models/models_skybox.c | 6 +++--- examples/models/models_yaw_pitch_roll.c | 6 +++--- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_model_shader.c | 4 ++-- examples/shaders/shaders_postprocessing.c | 2 +- 12 files changed, 44 insertions(+), 33 deletions(-) (limited to 'examples') diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index c8d62c46..ac24188e 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -31,7 +31,7 @@ int main() // NOTE: By default each cube is mapped to one part of texture atlas Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index d131b127..e0475f18 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -29,7 +29,7 @@ int main() Mesh mesh = GenMeshHeightmap(image, (Vector3){ 16, 8, 16 }); // Generate heightmap mesh (RAM and VRAM) Model model = LoadModelFromMesh(mesh); // Load model from generated mesh - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -8.0f, 0.0f, -8.0f }; // Define model position UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index f93c7a68..adb4762b 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -38,16 +38,16 @@ int main() // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); - MeshTangents(&model.mesh); - model.material = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); + MeshTangents(&model.meshes[0]); + model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); // Define lights attributes // NOTE: Shader is passed to every light on creation to define shader bindings internally 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.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.material.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.material.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){ -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) }; SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode @@ -64,7 +64,7 @@ int main() // Send to material PBR shader camera view position float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; - SetShaderValue(model.material.shader, model.material.shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); + SetShaderValue(model.materials[0].shader, model.materials[0].shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d64889bd..2b4b75ab 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -39,7 +39,7 @@ int main() models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); // Set checked texture as default diffuse component for all models material - for (int i = 0; i < NUM_MODELS; i++) models[i].material.maps[MAP_DIFFUSE].texture = texture; + for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MAP_DIFFUSE].texture = texture; // Define the camera to look into our 3d world Camera camera = {{ 5.0f, 5.0f, 5.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; @@ -65,6 +65,17 @@ int main() { currentModel = (currentModel + 1)%NUM_MODELS; // Cycle between the textures } + + if (IsKeyPressed(KEY_RIGHT)) + { + currentModel++; + if (currentModel >= NUM_MODELS) currentModel = 0; + } + else if (IsKeyPressed(KEY_LEFT)) + { + currentModel--; + if (currentModel < 0) currentModel = NUM_MODELS - 1; + } //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 9b12e98c..42028829 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -26,7 +26,7 @@ int main() // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position + camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position camera.target = (Vector3){ 0.0f, 8.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.6f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y @@ -36,10 +36,10 @@ int main() Model tower = LoadModel("resources/models/turret.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load model texture - tower.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + tower.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 towerPos = { 0.0f, 0.0f, 0.0f }; // Set model position - BoundingBox towerBBox = MeshBoundingBox(tower.mesh); // Get mesh bounding box + BoundingBox towerBBox = MeshBoundingBox(tower.meshes[0]); // Get mesh bounding box bool hitMeshBBox = false; bool hitTriangle = false; diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c index 7ec2d3f0..0251fd3e 100644 --- a/examples/models/models_obj_loading.c +++ b/examples/models/models_obj_loading.c @@ -30,7 +30,7 @@ int main() Model model = LoadModel("resources/models/castle.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse 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 diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index 15f79549..ffa609e0 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -27,13 +27,13 @@ int main() Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position - BoundingBox bounds = MeshBoundingBox(model.mesh); // Set model bounds - bool selected = false; // Selected object flag + Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position + BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds + bool selected = false; // Selected object flag - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode char objFilename[64] = "turret.obj"; @@ -54,15 +54,15 @@ int main() { if (IsFileExtension(droppedFiles[0], ".obj")) { - UnloadMesh(&model.mesh); - model.mesh = LoadMesh(droppedFiles[0]); - bounds = MeshBoundingBox(model.mesh); + UnloadMesh(&model.meshes[0]); + model.meshes[0] = LoadMesh(droppedFiles[0]); + bounds = MeshBoundingBox(model.meshes[0]); } else if (IsFileExtension(droppedFiles[0], ".png")) { UnloadTexture(texture); texture = LoadTexture(droppedFiles[0]); - model.material.maps[MAP_DIFFUSE].texture = texture; + model.materials[0].maps[MAP_DIFFUSE].texture = texture; } strcpy(objFilename, GetFileName(droppedFiles[0])); diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index c7f76ecf..759c79c6 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -29,8 +29,8 @@ int main() // Load skybox shader and set required locations // NOTE: Some locations are automatically set at shader loading - skybox.material.shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); - SetShaderValue(skybox.material.shader, GetShaderLocation(skybox.material.shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); + skybox.materials[0].shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); + SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); // Load cubemap shader and setup required shader locations Shader shdrCubemap = LoadShader("resources/shaders/cubemap.vs", "resources/shaders/cubemap.fs"); @@ -41,7 +41,7 @@ int main() // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE: New texture is generated rendering to texture, shader computes the sphre->cube coordinates mapping - skybox.material.maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); + skybox.materials[0].maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); UnloadTexture(texHDR); // Texture not required anymore, cubemap already generated UnloadShader(shdrCubemap); // Unload cubemap generation shader, not required anymore diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 88b0a610..7eaaf0be 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -37,10 +37,10 @@ int main() RenderTexture2D framebuffer = LoadRenderTexture(192, 192); // Model loading - Model model = LoadModel("resources/plane.obj"); // Load OBJ model - model.material.maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture + Model model = LoadModel("resources/plane.obj"); // Load OBJ model + model.materials[0].maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture - GenTextureMipmaps(&model.material.maps[MAP_DIFFUSE].texture); + GenTextureMipmaps(&model.materials[0].maps[MAP_DIFFUSE].texture); Camera camera = { 0 }; camera.position = (Vector3){ 0.0f, 60.0f, -120.0f };// Camera position perspective diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index fbfd82d0..74b9e771 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -45,7 +45,7 @@ int main() Model model = LoadModel("resources/models/barracks.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/barracks_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index 6c64f0ef..2717c192 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -50,8 +50,8 @@ int main() // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader Shader shader = LoadShader(0, FormatText("resources/shaders/glsl%i/grayscale.fs", GLSL_VERSION)); - model.material.shader = shader; // Set shader effect to 3d model - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + model.materials[0].shader = shader; // Set shader effect to 3d model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index f8483563..7c146419 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -74,7 +74,7 @@ int main() Model model = LoadModel("resources/models/church.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position -- cgit v1.2.3 From 2217c04ecf426cc2011c864fa788354ffa1de307 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 20:54:45 +0100 Subject: Update standard_lighting.c --- examples/others/standard_lighting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/others/standard_lighting.c b/examples/others/standard_lighting.c index 7034aa35..c37ae151 100644 --- a/examples/others/standard_lighting.c +++ b/examples/others/standard_lighting.c @@ -113,7 +113,7 @@ int main() material.maps[MAP_DIFFUSE].color = WHITE; material.maps[MAP_SPECULAR].color = WHITE; - model.material = material; // Apply material to model + model.materials[0] = material; // Apply material to model Light spotLight = CreateLight(LIGHT_SPOT, (Vector3){3.0f, 5.0f, 2.0f}, (Color){255, 255, 255, 255}); spotLight->target = (Vector3){0.0f, 0.0f, 0.0f}; -- cgit v1.2.3 From 26fb2e0f3a5047dd77bfea82543a341aa25dda7a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 00:15:14 +0200 Subject: Update cube.obj --- examples/models/resources/models/cube.obj | 44 +++++-------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) (limited to 'examples') diff --git a/examples/models/resources/models/cube.obj b/examples/models/resources/models/cube.obj index 0e9d6597..bf7e3bee 100644 --- a/examples/models/resources/models/cube.obj +++ b/examples/models/resources/models/cube.obj @@ -1,7 +1,7 @@ # reference material -mtllib myCube.mtl +#mtllib cube.mtl -# object Pau_Box +# object box # vertex (XZY) v 5.5 0 1.5 @@ -14,85 +14,55 @@ v 5.5 3 -1.5 v 8.5 3 -1.5 # normals (XYZ) -# red vn 0 -1 0 -#blue vn 0 1 0 -#top vn 0 0 1 -#yellow vn 1 0 0 -#bottom vn 0 0 -1 -#green vn -1 0 0 - # UVs (XY) -# yellow (1234) vt 0.5 0 0 vt 1 0 0 vt 1 0.5 0 vt 0.5 0.5 0 -# red (5678) vt 0.5 0.5 0 vt 1 0.5 0 vt 0.5 1 0 vt 1 1 0 -#bottom (9101112) vt 0 0.5 0 vt 1 0.5 0 vt 1 0 0 vt 0 0 0 -#top (13141516) vt 0 0.5 0 vt 1 0.5 0 vt 1 1 0 vt 0 1 0 -#green (17181920) vt 0.5 0 0 vt 0 0 0 vt 0 0.5 0 vt 0.5 0.5 0 -#blue (21222324) vt 0 0.5 0 vt 0.5 0.5 0 vt 0.5 1 0 vt 0 1 0 # merger -g Pau_Box +g box # reference material -usemtl Material_1 - -# bottom -f 1/9/1 3/10/1 4/11/1 -f 4/11/1 2/12/1 1/9/1 +#usemtl mat01 -# top +# faces +f 1/9/1 3/10/1 4/11/1 +f 4/11/1 2/12/1 1/9/1 f 5/13/2 6/14/2 8/15/2 f 8/15/2 7/16/2 5/13/2 - -# front-yellow f 1/17/6 2/18/6 6/19/6 f 6/19/6 5/20/6 1/17/6 - -# right-blue f 2/6/1 4/5/1 8/7/1 f 8/7/1 6/8/1 2/6/1 - -# back-green f 4/2/3 3/1/3 7/4/3 f 7/4/3 8/3/3 4/2/3 - -# left-red f 3/22/5 1/21/5 5/24/5 f 5/24/5 7/23/5 3/22/5 - - - - - - - -- cgit v1.2.3 From 38a13b76d13c6e4f6f6c02bfd63900de56de4a42 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:13:42 +0200 Subject: Corrected issue with LoadMesh() --- examples/models/models_obj_loading.c | 2 +- examples/models/models_obj_viewer.c | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'examples') diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c index 0251fd3e..74e7d08a 100644 --- a/examples/models/models_obj_loading.c +++ b/examples/models/models_obj_loading.c @@ -59,7 +59,7 @@ int main() DrawGizmo(position); // Draw gizmo EndMode3D(); - + DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); DrawFPS(10, 10); diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index ffa609e0..0581df34 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -28,11 +28,11 @@ int main() Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - + Vector3 position = { 0.0, 0.0, 0.0 }; // 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 - + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode char objFilename[64] = "turret.obj"; @@ -49,13 +49,13 @@ int main() { int count = 0; char **droppedFiles = GetDroppedFiles(&count); - + if (count == 1) { if (IsFileExtension(droppedFiles[0], ".obj")) { - UnloadMesh(&model.meshes[0]); - model.meshes[0] = LoadMesh(droppedFiles[0]); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); + model.meshes = LoadMeshes(droppedFiles[0], &model.meshCount); bounds = MeshBoundingBox(model.meshes[0]); } else if (IsFileExtension(droppedFiles[0], ".png")) @@ -67,12 +67,12 @@ int main() strcpy(objFilename, GetFileName(droppedFiles[0])); } - + ClearDroppedFiles(); // Clear internal buffers } - + UpdateCamera(&camera); - + // Select model on mouse click if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { @@ -93,21 +93,21 @@ int main() DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture DrawGrid(20.0, 10.0); // Draw a grid - + if (selected) DrawBoundingBox(bounds, GREEN); - + EndMode3D(); - + DrawText("Free camera default controls:", 10, 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); - + DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); @@ -119,7 +119,7 @@ int main() UnloadModel(model); // Unload model ClearDroppedFiles(); // Clear internal buffers - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- -- cgit v1.2.3 From 92733d6695e0cdab3b42972f2cd6ed48d98ec689 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:15:56 +0200 Subject: BIG UPDATE: New models functions for animations! Multiple functions added and some reviewed to adapt to the new multi-mesh, multi-material and animated models. --- examples/models/models_animation.c | 101 +++ examples/models/resources/guy/guy.blend | Bin 0 -> 665304 bytes examples/models/resources/guy/guy.iqm | Bin 0 -> 39408 bytes examples/models/resources/guy/guyanim.iqm | Bin 0 -> 18244 bytes examples/models/resources/guy/guytex.png | Bin 0 -> 302388 bytes examples/others/iqm_loader/models_iqm_animation.c | 98 --- examples/others/iqm_loader/resources/guy.blend | Bin 665304 -> 0 bytes examples/others/iqm_loader/resources/guy.iqm | Bin 39408 -> 0 bytes examples/others/iqm_loader/resources/guyanim.iqm | Bin 18244 -> 0 bytes examples/others/iqm_loader/resources/guytex.png | Bin 302388 -> 0 bytes examples/others/iqm_loader/riqm.h | 739 +--------------------- src/models.c | 652 ++++++++++++++----- src/raylib.h | 33 +- 13 files changed, 617 insertions(+), 1006 deletions(-) create mode 100644 examples/models/models_animation.c create mode 100644 examples/models/resources/guy/guy.blend create mode 100644 examples/models/resources/guy/guy.iqm create mode 100644 examples/models/resources/guy/guyanim.iqm create mode 100644 examples/models/resources/guy/guytex.png delete mode 100644 examples/others/iqm_loader/models_iqm_animation.c delete mode 100644 examples/others/iqm_loader/resources/guy.blend delete mode 100644 examples/others/iqm_loader/resources/guy.iqm delete mode 100644 examples/others/iqm_loader/resources/guyanim.iqm delete mode 100644 examples/others/iqm_loader/resources/guytex.png (limited to 'examples') diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c new file mode 100644 index 00000000..a75241b3 --- /dev/null +++ b/examples/models/models_animation.c @@ -0,0 +1,101 @@ +/******************************************************************************************* +* +* raylib [models] example - Load 3d model with animations and play them +* +* 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) +* +* Copyright (c) 2019 Ramon Santamaria (@raysan5) and @culacant +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.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/guy/guy.iqm"); // Load the animated model mesh and basic data + Texture2D texture = LoadTexture("resources/guy/guytex.png"); // Load model texture and set material + SetMaterialTexture(&model.materials[0], MAP_DIFFUSE, texture); // Set model material map texture + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + + // Load animation data + int animsCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/guy/guyanim.iqm", &animsCount); + int animFrameCounter = 0; + + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + + 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 + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + // Play animation when spacebar is held down + if (IsKeyDown(KEY_SPACE)) + { + animFrameCounter++; + UpdateModelAnimation(model, anims[0], animFrameCounter); + if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); + + for (int i = 0; i < model.boneCount; i++) + { + DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); + } + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON); + DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + // Unload model animations data + for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + + UnloadModel(model); // Unload model + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/resources/guy/guy.blend b/examples/models/resources/guy/guy.blend new file mode 100644 index 00000000..3880467d Binary files /dev/null and b/examples/models/resources/guy/guy.blend differ diff --git a/examples/models/resources/guy/guy.iqm b/examples/models/resources/guy/guy.iqm new file mode 100644 index 00000000..36bed5e0 Binary files /dev/null and b/examples/models/resources/guy/guy.iqm differ diff --git a/examples/models/resources/guy/guyanim.iqm b/examples/models/resources/guy/guyanim.iqm new file mode 100644 index 00000000..824a68a3 Binary files /dev/null and b/examples/models/resources/guy/guyanim.iqm differ diff --git a/examples/models/resources/guy/guytex.png b/examples/models/resources/guy/guytex.png new file mode 100644 index 00000000..7f813552 Binary files /dev/null and b/examples/models/resources/guy/guytex.png differ diff --git a/examples/others/iqm_loader/models_iqm_animation.c b/examples/others/iqm_loader/models_iqm_animation.c deleted file mode 100644 index 18dd8577..00000000 --- a/examples/others/iqm_loader/models_iqm_animation.c +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************************* -* -* raylib [models] example - Load IQM 3d model with animations and play them -* -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2018 @culacant and @raysan5 -* -********************************************************************************************/ - -#include "raylib.h" - -#define RIQM_IMPLEMENTATION -#include "riqm.h" - -int main() -{ - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - iqm animation"); - - // Define the camera to look into our 3d world - Camera camera = { 0 }; - camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.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 - - // Load the animated model mesh and basic data - AnimatedModel model = LoadAnimatedModel("resources/guy.iqm"); - - // Load model texture and set material - // NOTE: There is only 1 mesh and 1 material (both at index 0), thats what the 2 0's are - model = AnimatedModelAddTexture(model, "resources/guytex.png"); // REPLACE! - model = SetMeshMaterial(model, 0, 0); // REPLACE! - - // Load animation data - Animation anim = LoadAnimationFromIQM("resources/guyanim.iqm"); - - int animFrameCounter = 0; - - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - - 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 - //---------------------------------------------------------------------------------- - UpdateCamera(&camera); - - // Play animation when spacebar is held down - if (IsKeyDown(KEY_SPACE)) - { - animFrameCounter++; - AnimateModel(model, anim, animFrameCounter); // Animate the model with animation data and frame - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - BeginMode3D(camera); - - DrawAnimatedModel(model, Vector3Zero(), 1.0f, WHITE); // Draw animated model - - DrawGrid(10, 1.0f); // Draw a grid - - EndMode3D(); - - DrawText("PRESS SPACE to PLAY IQM MODEL ANIMATION", 10, 10, 20, MAROON); - - DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadAnimation(anim); // Unload animation data - UnloadAnimatedModel(model); // Unload animated model - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/others/iqm_loader/resources/guy.blend b/examples/others/iqm_loader/resources/guy.blend deleted file mode 100644 index 3880467d..00000000 Binary files a/examples/others/iqm_loader/resources/guy.blend and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guy.iqm b/examples/others/iqm_loader/resources/guy.iqm deleted file mode 100644 index 36bed5e0..00000000 Binary files a/examples/others/iqm_loader/resources/guy.iqm and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guyanim.iqm b/examples/others/iqm_loader/resources/guyanim.iqm deleted file mode 100644 index 824a68a3..00000000 Binary files a/examples/others/iqm_loader/resources/guyanim.iqm and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guytex.png b/examples/others/iqm_loader/resources/guytex.png deleted file mode 100644 index 7f813552..00000000 Binary files a/examples/others/iqm_loader/resources/guytex.png and /dev/null differ diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h index 694e3a95..41ef8a14 100644 --- a/examples/others/iqm_loader/riqm.h +++ b/examples/others/iqm_loader/riqm.h @@ -49,67 +49,13 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -#define JOINT_NAME_LENGTH 32 // Joint name string length -#define MESH_NAME_LENGTH 32 // Mesh name string length - -typedef struct Joint { - char name[JOINT_NAME_LENGTH]; - int parent; -} Joint; - -typedef struct Pose { - Vector3 translation; - Quaternion rotation; - Vector3 scale; -} Pose; - -typedef struct Animation { - int jointCount; // Number of joints (bones) - Joint *joints; // Joints array - // NOTE: Joints in anims do not have names - - int frameCount; // Number of animation frames - float framerate; // Frame change speed - - Pose **framepose; // Poses array by frame (and one pose by joint) -} Animation; - -// Animated Model type -typedef struct AnimatedModel { - Matrix transform; // Local transform matrix - - int meshCount; // Number of meshes - Mesh *meshes; // Meshes array - - int materialCount; // Number of materials - Material *materials; // Materials array - - int *meshMaterialId; // Mesh materials ids - - // Animation required data - int jointCount; // Number of joints (and keyposes) - Joint *joints; // Mesh joints (bones) - Pose *basepose; // Mesh base-poses by joint -} AnimatedModel; +#define BONE_NAME_LENGTH 32 // BoneInfo name string length +#define MESH_NAME_LENGTH 32 // Mesh name string length //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -// Loading/Unloading functions -RIQMDEF AnimatedModel LoadAnimatedModel(const char *filename); -RIQMDEF void UnloadAnimatedModel(AnimatedModel model); -RIQMDEF Animation LoadAnimation(const char *filename); -RIQMDEF void UnloadAnimation(Animation anim); - -RIQMDEF AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename); // GENERIC! -RIQMDEF AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid); // GENERIC! - -// Usage functionality -RIQMDEF bool CheckSkeletonsMatch(AnimatedModel model, Animation anim); -RIQMDEF void AnimateModel(AnimatedModel model, Animation anim, int frame); -RIQMDEF void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint); -RIQMDEF void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); #endif // RIQM_H @@ -133,90 +79,6 @@ RIQMDEF void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number -#define IQM_VERSION 2 // only IQM version 2 supported -#define ANIMJOINTNAME "ANIMJOINT" // default joint name (used in Animation) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// iqm file structs -typedef struct IQMHeader { - char magic[16]; - unsigned int version; - unsigned int filesize; - unsigned int flags; - unsigned int num_text, ofs_text; - unsigned int num_meshes, ofs_meshes; - unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; - unsigned int num_triangles, ofs_triangles, ofs_adjacency; - unsigned int num_joints, ofs_joints; - unsigned int num_poses, ofs_poses; - unsigned int num_anims, ofs_anims; - unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; - unsigned int num_comment, ofs_comment; - unsigned int num_extensions, ofs_extensions; -} IQMHeader; - -typedef struct IQMMesh { - unsigned int name; - unsigned int material; - unsigned int first_vertex, num_vertexes; - unsigned int first_triangle, num_triangles; -} IQMMesh; - -typedef struct IQMTriangle { - unsigned int vertex[3]; -} IQMTriangle; - -typedef struct IQMAdjacency { // adjacency unused by default - unsigned int triangle[3]; -} IQMAdjacency; - -typedef struct IQMJoint { - unsigned int name; - int parent; - float translate[3], rotate[4], scale[3]; -} IQMJoint; - -typedef struct IQMPose { - int parent; - unsigned int mask; - float channeloffset[10]; - float channelscale[10]; -} IQMPose; - -typedef struct IQMAnim { - unsigned int name; - unsigned int first_frame, num_frames; - float framerate; - unsigned int flags; -} IQMAnim; - -typedef struct IQMVertexArray { - unsigned int type; - unsigned int flags; - unsigned int format; - unsigned int size; - unsigned int offset; -} IQMVertexArray; - -typedef struct IQMBounds { // bounds unused by default - float bbmin[3], bbmax[3]; - float xyradius, radius; -} IQMBounds; - - -typedef enum { - IQM_POSITION = 0, - IQM_TEXCOORD = 1, - IQM_NORMAL = 2, - IQM_TANGENT = 3, // tangents unused by default - IQM_BLENDINDEXES = 4, - IQM_BLENDWEIGHTS = 5, - IQM_COLOR = 6, // vertex colors unused by default - IQM_CUSTOM = 0x10 // custom vertex values unused by default -} IQMVertexType; //---------------------------------------------------------------------------------- // Global Variables Definition @@ -225,609 +87,12 @@ typedef enum { //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static AnimatedModel LoadIQM(const char *filename); #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif -// Load .iqm file and initialize animated model -AnimatedModel LoadAnimatedModel(const char *filename) -{ - AnimatedModel out = LoadIQM(filename); - - for (int i = 0; i < out.meshCount; i++) rlLoadMesh(&out.meshes[i], false); - - out.transform = MatrixIdentity(); - out.meshMaterialId = malloc(sizeof(int)*out.meshCount); - out.materials = NULL; - out.materialCount = 0; - - for (int i = 0; i < out.meshCount; i++) out.meshMaterialId[i] = -1; - - return out; -} - -// Add a texture to an animated model -AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename) -{ - Texture2D texture = LoadTexture(filename); - - model.materials = realloc(model.materials, sizeof(Material)*(model.materialCount + 1)); - model.materials[model.materialCount] = LoadMaterialDefault(); - model.materials[model.materialCount].maps[MAP_DIFFUSE].texture = texture; - model.materialCount++; - - return model; -} - -// Set the material for a meshes -AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid) -{ - if (meshid > model.meshCount) - { - TraceLog(LOG_WARNING, "MeshId greater than meshCount\n"); - return model; - } - - if (textureid > model.materialCount) - { - TraceLog(LOG_WARNING,"textureid greater than materialCount\n"); - return model; - } - - model.meshMaterialId[meshid] = textureid; - - return model; -} - -// Load animations from a .iqm file -Animation LoadAnimationFromIQM(const char *filename) -{ - Animation animation = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - iqmFile = fopen(filename,"rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return animation; - } - - // header - fread(&iqm, sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return animation; - } - - if (iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return animation; - } - - // header - if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will get loaded"); - - // joints - IQMPose *poses; - poses = malloc(sizeof(IQMPose)*iqm.num_poses); - fseek(iqmFile, iqm.ofs_poses, SEEK_SET); - fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); - - animation.jointCount = iqm.num_poses; - animation.joints = malloc(sizeof(Joint)*iqm.num_poses); - - for (int j = 0; j < iqm.num_poses; j++) - { - strcpy(animation.joints[j].name, ANIMJOINTNAME); - animation.joints[j].parent = poses[j].parent; - } - - // animations - IQMAnim anim = {0}; - fseek(iqmFile, iqm.ofs_anims, SEEK_SET); - fread(&anim, sizeof(IQMAnim), 1, iqmFile); - - animation.frameCount = anim.num_frames; - animation.framerate = anim.framerate; - - // frameposes - unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); - fseek(iqmFile, iqm.ofs_frames, SEEK_SET); - fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); - - animation.framepose = malloc(sizeof(Pose*)*anim.num_frames); - for (int j = 0; j < anim.num_frames; j++) animation.framepose[j] = malloc(sizeof(Pose)*iqm.num_poses); - - int dcounter = anim.first_frame*iqm.num_framechannels; - - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < iqm.num_poses; i++) - { - animation.framepose[frame][i].translation.x = poses[i].channeloffset[0]; - - if (poses[i].mask & 0x01) - { - animation.framepose[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; - dcounter++; - } - - animation.framepose[frame][i].translation.y = poses[i].channeloffset[1]; - - if (poses[i].mask & 0x02) - { - animation.framepose[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; - dcounter++; - } - - animation.framepose[frame][i].translation.z = poses[i].channeloffset[2]; - - if (poses[i].mask & 0x04) - { - animation.framepose[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; - dcounter++; - } - - animation.framepose[frame][i].rotation.x = poses[i].channeloffset[3]; - - if (poses[i].mask & 0x08) - { - animation.framepose[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; - dcounter++; - } - - animation.framepose[frame][i].rotation.y = poses[i].channeloffset[4]; - - if (poses[i].mask & 0x10) - { - animation.framepose[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; - dcounter++; - } - - animation.framepose[frame][i].rotation.z = poses[i].channeloffset[5]; - - if (poses[i].mask & 0x20) - { - animation.framepose[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; - dcounter++; - } - - animation.framepose[frame][i].rotation.w = poses[i].channeloffset[6]; - - if (poses[i].mask & 0x40) - { - animation.framepose[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; - dcounter++; - } - - animation.framepose[frame][i].scale.x = poses[i].channeloffset[7]; - - if (poses[i].mask & 0x80) - { - animation.framepose[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; - dcounter++; - } - - animation.framepose[frame][i].scale.y = poses[i].channeloffset[8]; - - if (poses[i].mask & 0x100) - { - animation.framepose[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; - dcounter++; - } - - animation.framepose[frame][i].scale.z = poses[i].channeloffset[9]; - - if (poses[i].mask & 0x200) - { - animation.framepose[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; - dcounter++; - } - - animation.framepose[frame][i].rotation = QuaternionNormalize(animation.framepose[frame][i].rotation); - } - } - - // Build frameposes - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < animation.jointCount; i++) - { - if (animation.joints[i].parent >= 0) - { - animation.framepose[frame][i].rotation = QuaternionMultiply(animation.framepose[frame][animation.joints[i].parent].rotation, animation.framepose[frame][i].rotation); - animation.framepose[frame][i].translation = Vector3RotateByQuaternion(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].rotation); - animation.framepose[frame][i].translation = Vector3Add(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].translation); - animation.framepose[frame][i].scale = Vector3MultiplyV(animation.framepose[frame][i].scale, animation.framepose[frame][animation.joints[i].parent].scale); - } - } - } - - free(framedata); - free(poses); - - fclose(iqmFile); - - return animation; -} - -// Unload animated model -void UnloadAnimatedModel(AnimatedModel model) -{ - free(model.materials); - free(model.meshMaterialId); - free(model.joints); - free(model.basepose); - - for (int i = 0; i < model.meshCount; i++) rlUnloadMesh(&model.meshes[i]); - - free(model.meshes); -} - -// Unload animation -void UnloadAnimation(Animation anim) -{ - free(anim.joints); - free(anim.framepose); - - for (int i = 0; i < anim.frameCount; i++) free(anim.framepose[i]); -} - -// Check if skeletons match, only parents and jointCount are checked -bool CheckSkeletonsMatch(AnimatedModel model, Animation anim) -{ - if (model.jointCount != anim.jointCount) return 0; - - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent != anim.joints[i].parent) return 0; - } - - return 1; -} - -// Calculate the animated vertex positions and normals based on an animation at a given frame -void AnimateModel(AnimatedModel model, Animation anim, int frame) -{ - if (frame >= anim.frameCount) frame = frame%anim.frameCount; - - for (int m = 0; m < model.meshCount; m++) - { - Vector3 outv = {0}; - Vector3 outn = {0}; - - Vector3 baset = {0}; - Quaternion baser = {0}; - Vector3 bases = {0}; - - Vector3 outt = {0}; - Quaternion outr = {0}; - Vector3 outs = {0}; - - int vcounter = 0; - int wcounter = 0; - int weightId = 0; - - for (int i = 0; i < model.meshes[m].vertexCount; i++) - { - weightId = model.meshes[m].weightId[wcounter]; - baset = model.basepose[weightId].translation; - baser = model.basepose[weightId].rotation; - bases = model.basepose[weightId].scale; - outt = anim.framepose[frame][weightId].translation; - outr = anim.framepose[frame][weightId].rotation; - outs = anim.framepose[frame][weightId].scale; - - // vertices - // NOTE: We use meshes.baseVertices (default position) to calculate meshes.vertices (animated position) - outv = (Vector3){ model.meshes[m].baseVertices[vcounter], model.meshes[m].baseVertices[vcounter + 1], model.meshes[m].baseVertices[vcounter + 2] }; - outv = Vector3MultiplyV(outv, outs); - outv = Vector3Subtract(outv, baset); - outv = Vector3RotateByQuaternion(outv, QuaternionMultiply(outr, QuaternionInvert(baser))); - outv = Vector3Add(outv, outt); - model.meshes[m].vertices[vcounter] = outv.x; - model.meshes[m].vertices[vcounter + 1] = outv.y; - model.meshes[m].vertices[vcounter + 2] = outv.z; - - // normals - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - outn = (Vector3){ model.meshes[m].baseNormals[vcounter], model.meshes[m].baseNormals[vcounter + 1], model.meshes[m].baseNormals[vcounter + 2] }; - outn = Vector3RotateByQuaternion(outn, QuaternionMultiply(outr, QuaternionInvert(baser))); - model.meshes[m].normals[vcounter] = outn.x; - model.meshes[m].normals[vcounter + 1] = outn.y; - model.meshes[m].normals[vcounter + 2] = outn.z; - vcounter += 3; - wcounter += 4; - } - } -} - -// Draw an animated model -void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint) -{ - Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 1.0f, 0.0f,0.0f }; - - DrawAnimatedModelEx(model, position, rotationAxis, -90.0f, vScale, tint); -} - -// Draw an animated model with extended parameters -void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) -{ - if (model.materialCount == 0) - { - TraceLog(LOG_WARNING,"No materials set, can't draw animated meshes\n"); - return; - } - - Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); - - Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.transform = MatrixMultiply(model.transform, matTransform); - - for (int i = 0; i < model.meshCount; i++) - { - rlUpdateMesh(model.meshes[i], 0, model.meshes[i].vertexCount); // Update vertex position - rlUpdateMesh(model.meshes[i], 2, model.meshes[i].vertexCount); // Update vertex normals - rlDrawMesh(model.meshes[i], model.materials[model.meshMaterialId[i]], model.transform); // Draw meshes - } -} - -// Load animated model meshes from IQM file -static AnimatedModel LoadIQM(const char *filename) -{ - AnimatedModel model = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - IQMMesh *imesh; - IQMTriangle *tri; - IQMVertexArray *va; - IQMJoint *ijoint; - - float *vertex; - float *normal; - float *text; - char *blendi; - unsigned char *blendw; - - iqmFile = fopen(filename, "rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return model; - } - - // header - fread(&iqm,sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return model; - } - - if(iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return model; - } - - // meshes - imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); - fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); - fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); - - model.meshCount = iqm.num_meshes; - model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); - - char name[MESH_NAME_LENGTH]; - - for (int i = 0; i < iqm.num_meshes; i++) - { - fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); - fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... - model.meshes[i].vertexCount = imesh[i].num_vertexes; - - model.meshes[i].baseVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base position - model.meshes[i].baseNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base normal - - model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); - model.meshes[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4); - model.meshes[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4); - - model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); - - // What we actually process for rendering, should be updated transforming meshes.vertices and meshes.normals - model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); - model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); - } - - // tris - tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); - fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); - fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int tcounter = 0; - - for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) - { - // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around - model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; - tcounter += 3; - } - } - - // vertarrays - va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); - fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); - fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); - - for (int i = 0; i < iqm.num_vertexarrays; i++) - { - switch (va[i].type) - { - case IQM_POSITION: - { - vertex = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].vertices[vcounter] = vertex[i]; - model.meshes[m].baseVertices[vcounter] = vertex[i]; - vcounter++; - } - } - } break; - case IQM_NORMAL: - { - normal = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].normals[vcounter] = normal[i]; - model.meshes[m].baseNormals[vcounter] = normal[i]; - vcounter++; - } - } - } break; - case IQM_TEXCOORD: - { - text = malloc(sizeof(float)*iqm.num_vertexes*2); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) - { - model.meshes[m].texcoords[vcounter] = text[i]; - vcounter++; - } - } - } break; - case IQM_BLENDINDEXES: - { - blendi = malloc(sizeof(char)*iqm.num_vertexes*4); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightId[vcounter] = blendi[i]; - vcounter++; - } - } - } break; - case IQM_BLENDWEIGHTS: - { - blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); - fseek(iqmFile,va[i].offset,SEEK_SET); - fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightBias[vcounter] = blendw[i]/255.0f; - vcounter++; - } - } - } break; - } - } - - // joints, include base poses - ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); - fseek(iqmFile, iqm.ofs_joints, SEEK_SET); - fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); - - model.jointCount = iqm.num_joints; - model.joints = malloc(sizeof(Joint)*iqm.num_joints); - model.basepose = malloc(sizeof(Pose)*iqm.num_joints); - - for (int i = 0; i < iqm.num_joints; i++) - { - // joints - model.joints[i].parent = ijoint[i].parent; - fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); - fread(model.joints[i].name,sizeof(char)*JOINT_NAME_LENGTH, 1, iqmFile); - - // basepose - model.basepose[i].translation.x = ijoint[i].translate[0]; - model.basepose[i].translation.y = ijoint[i].translate[1]; - model.basepose[i].translation.z = ijoint[i].translate[2]; - - model.basepose[i].rotation.x = ijoint[i].rotate[0]; - model.basepose[i].rotation.y = ijoint[i].rotate[1]; - model.basepose[i].rotation.z = ijoint[i].rotate[2]; - model.basepose[i].rotation.w = ijoint[i].rotate[3]; - - model.basepose[i].scale.x = ijoint[i].scale[0]; - model.basepose[i].scale.y = ijoint[i].scale[1]; - model.basepose[i].scale.z = ijoint[i].scale[2]; - } - - // build base pose - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent >= 0) - { - model.basepose[i].rotation = QuaternionMultiply(model.basepose[model.joints[i].parent].rotation, model.basepose[i].rotation); - model.basepose[i].translation = Vector3RotateByQuaternion(model.basepose[i].translation, model.basepose[model.joints[i].parent].rotation); - model.basepose[i].translation = Vector3Add(model.basepose[i].translation, model.basepose[model.joints[i].parent].translation); - model.basepose[i].scale = Vector3MultiplyV(model.basepose[i].scale, model.basepose[model.joints[i].parent].scale); - } - } - fclose(iqmFile); - free(imesh); - free(tri); - free(va); - free(vertex); - free(normal); - free(text); - free(blendi); - free(blendw); - free(ijoint); - return model; -} #endif diff --git a/src/models.c b/src/models.c index 239ab5d8..e437a0ab 100644 --- a/src/models.c +++ b/src/models.c @@ -697,6 +697,18 @@ void UnloadModel(Model model) TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } +// Load meshes from model file +Mesh *LoadMeshes(const char *fileName, int *meshCount) +{ + Mesh *meshes = NULL; + int count = 0; + + // TODO: Load meshes from file (OBJ, IQM, GLTF) + + *meshCount = count; + return meshes; +} + // Unload mesh from memory (RAM and/or VRAM) void UnloadMesh(Mesh *mesh) { @@ -759,6 +771,386 @@ void ExportMesh(Mesh mesh, const char *fileName) else TraceLog(LOG_WARNING, "Mesh could not be exported."); } +// Load materials from model file +Material *LoadMaterials(const char *fileName, int *materialCount) +{ + Material *materials = NULL; + unsigned int count = 0; + + // TODO: Support IQM and GLTF for materials parsing + +#if defined(SUPPORT_FILEFORMAT_MTL) + if (IsFileExtension(fileName, ".mtl")) + { + tinyobj_material_t *mats; + + int result = tinyobj_parse_mtl_file(&mats, &count, fileName); + + // TODO: Process materials to return + + tinyobj_materials_free(mats, count); + } +#else + TraceLog(LOG_WARNING, "[%s] Materials file not supported", fileName); +#endif + + // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL) + for (int i = 0; i < count; i++) materials[i].shader = GetShaderDefault(); + + *materialCount = count; + return materials; +} + +// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +Material LoadMaterialDefault(void) +{ + Material material = { 0 }; + + material.shader = GetShaderDefault(); + material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) + //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set + //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + + material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color + material.maps[MAP_SPECULAR].color = WHITE; // Specular color + + return material; +} + +// Unload material from memory +void UnloadMaterial(Material material) +{ + // Unload material shader (avoid unloading default shader, managed by raylib) + if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); + + // Unload loaded texture maps (avoid unloading default texture, managed by raylib) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + } +} + +// Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +// NOTE: Previous texture should be manually unloaded +void SetMaterialTexture(Material *material, int mapType, Texture2D texture) +{ + material->maps[mapType].texture = texture; +} + +// Set the material for a mesh +void SetModelMeshMaterial(Model *model, int meshId, int materialId) +{ + if (meshId >= model->meshCount) TraceLog(LOG_WARNING, "Mesh id greater than mesh count"); + else if (materialId >= model->materialCount) TraceLog(LOG_WARNING,"Material id greater than material count"); + else model->meshMaterial[meshId] = materialId; +} + +// Load model animations from file +ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) +{ + ModelAnimation *animations = (ModelAnimation *)malloc(1*sizeof(ModelAnimation)); + int count = 1; + + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported + + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + ModelAnimation animation = { 0 }; + + FILE *iqmFile; + IQMHeader iqm; + + iqmFile = fopen(filename,"rb"); + + if (!iqmFile) + { + TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); + } + + // header + fread(&iqm, sizeof(IQMHeader), 1, iqmFile); + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) + { + TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); + fclose(iqmFile); + } + + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); + fclose(iqmFile); + } + + // header + if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will be loaded"); + + // bones + IQMPose *poses; + poses = malloc(sizeof(IQMPose)*iqm.num_poses); + fseek(iqmFile, iqm.ofs_poses, SEEK_SET); + fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); + + animation.boneCount = iqm.num_poses; + animation.bones = malloc(sizeof(BoneInfo)*iqm.num_poses); + + for (int j = 0; j < iqm.num_poses; j++) + { + strcpy(animation.bones[j].name, "ANIMJOINTNAME"); + animation.bones[j].parent = poses[j].parent; + } + + // animations + IQMAnim anim = {0}; + fseek(iqmFile, iqm.ofs_anims, SEEK_SET); + fread(&anim, sizeof(IQMAnim), 1, iqmFile); + + animation.frameCount = anim.num_frames; + //animation.framerate = anim.framerate; + + // frameposes + unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); + fseek(iqmFile, iqm.ofs_frames, SEEK_SET); + fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); + + animation.framePoses = malloc(sizeof(Transform*)*anim.num_frames); + for (int j = 0; j < anim.num_frames; j++) animation.framePoses[j] = malloc(sizeof(Transform)*iqm.num_poses); + + int dcounter = anim.first_frame*iqm.num_framechannels; + + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < iqm.num_poses; i++) + { + animation.framePoses[frame][i].translation.x = poses[i].channeloffset[0]; + + if (poses[i].mask & 0x01) + { + animation.framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; + dcounter++; + } + + animation.framePoses[frame][i].translation.y = poses[i].channeloffset[1]; + + if (poses[i].mask & 0x02) + { + animation.framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; + dcounter++; + } + + animation.framePoses[frame][i].translation.z = poses[i].channeloffset[2]; + + if (poses[i].mask & 0x04) + { + animation.framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; + + if (poses[i].mask & 0x08) + { + animation.framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; + + if (poses[i].mask & 0x10) + { + animation.framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; + + if (poses[i].mask & 0x20) + { + animation.framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; + + if (poses[i].mask & 0x40) + { + animation.framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; + dcounter++; + } + + animation.framePoses[frame][i].scale.x = poses[i].channeloffset[7]; + + if (poses[i].mask & 0x80) + { + animation.framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; + dcounter++; + } + + animation.framePoses[frame][i].scale.y = poses[i].channeloffset[8]; + + if (poses[i].mask & 0x100) + { + animation.framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; + dcounter++; + } + + animation.framePoses[frame][i].scale.z = poses[i].channeloffset[9]; + + if (poses[i].mask & 0x200) + { + animation.framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; + dcounter++; + } + + animation.framePoses[frame][i].rotation = QuaternionNormalize(animation.framePoses[frame][i].rotation); + } + } + + // Build frameposes + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < animation.boneCount; i++) + { + if (animation.bones[i].parent >= 0) + { + animation.framePoses[frame][i].rotation = QuaternionMultiply(animation.framePoses[frame][animation.bones[i].parent].rotation, animation.framePoses[frame][i].rotation); + animation.framePoses[frame][i].translation = Vector3RotateByQuaternion(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].rotation); + animation.framePoses[frame][i].translation = Vector3Add(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].translation); + animation.framePoses[frame][i].scale = Vector3MultiplyV(animation.framePoses[frame][i].scale, animation.framePoses[frame][animation.bones[i].parent].scale); + } + } + } + + free(framedata); + free(poses); + + fclose(iqmFile); + + animations[0] = animation; + + *animCount = count; + return animations; +} + +// Update model animated vertex data (positions and normals) for a given frame +// NOTE: Updated data is uploaded to GPU +void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) +{ + if (frame >= anim.frameCount) frame = frame%anim.frameCount; + + for (int m = 0; m < model.meshCount; m++) + { + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; + + Vector3 inTranslation = { 0 }; + Quaternion inRotation = { 0 }; + Vector3 inScale = { 0 }; + + Vector3 outTranslation = { 0 }; + Quaternion outRotation = { 0 }; + Vector3 outScale = { 0 }; + + int vCounter = 0; + int boneCounter = 0; + int boneId = 0; + + for (int i = 0; i < model.meshes[m].vertexCount; i++) + { + boneId = model.meshes[m].boneIds[boneCounter]; + inTranslation = model.bindPose[boneId].translation; + inRotation = model.bindPose[boneId].rotation; + inScale = model.bindPose[boneId].scale; + outTranslation = anim.framePoses[frame][boneId].translation; + outRotation = anim.framePoses[frame][boneId].rotation; + outScale = anim.framePoses[frame][boneId].scale; + + // Vertices processing + // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position) + animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] }; + animVertex = Vector3MultiplyV(animVertex, outScale); + animVertex = Vector3Subtract(animVertex, inTranslation); + animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + animVertex = Vector3Add(animVertex, outTranslation); + model.meshes[m].animVertices[vCounter] = animVertex.x; + model.meshes[m].animVertices[vCounter + 1] = animVertex.y; + model.meshes[m].animVertices[vCounter + 2] = animVertex.z; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; + animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + model.meshes[m].animNormals[vCounter] = animNormal.x; + model.meshes[m].animNormals[vCounter + 1] = animNormal.y; + model.meshes[m].animNormals[vCounter + 2] = animNormal.z; + vCounter += 3; + + boneCounter += 4; + } + + // Upload new vertex data to GPU for model drawing + rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position + rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals + } +} + +// Unload animation data +void UnloadModelAnimation(ModelAnimation anim) +{ + for (int i = 0; i < anim.frameCount; i++) free(anim.framePoses[i]); + + free(anim.bones); + free(anim.framePoses); +} + +// Check model animation skeleton match +// NOTE: Only number of bones and parent connections are checked +bool IsModelAnimationValid(Model model, ModelAnimation anim) +{ + int result = true; + + if (model.boneCount != anim.boneCount) result = false; + else + { + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; } + } + } + + return result; +} + #if defined(SUPPORT_MESH_GENERATION) // Generate polygonal mesh Mesh GenMeshPoly(int sides, float radius) @@ -1807,59 +2199,124 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) } #endif // SUPPORT_MESH_GENERATION -// Load material data (from file) -Material LoadMaterial(const char *fileName) +// Compute mesh bounding box limits +// NOTE: minVertex and maxVertex should be transformed by model transform matrix +BoundingBox MeshBoundingBox(Mesh mesh) { - Material material = { 0 }; + // Get min and max vertex to construct bounds (AABB) + Vector3 minVertex = { 0 }; + Vector3 maxVertex = { 0 }; -#if defined(SUPPORT_FILEFORMAT_MTL) - if (IsFileExtension(fileName, ".mtl")) + if (mesh.vertices != NULL) { - tinyobj_material_t *materials; - unsigned int materialCount = 0; - - int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); - - // TODO: Process materials to return + minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; + maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - tinyobj_materials_free(materials, materialCount); + for (int i = 1; i < mesh.vertexCount; i++) + { + minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + } } -#else - TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); -#endif - // Our material uses the default shader (DIFFUSE, SPECULAR, NORMAL) - material.shader = GetShaderDefault(); + // Create the bounding box + BoundingBox box = { 0 }; + box.min = minVertex; + box.max = maxVertex; - return material; + return box; } -// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -Material LoadMaterialDefault(void) +// Compute mesh tangents +// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates +// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html +void MeshTangents(Mesh *mesh) { - Material material = { 0 }; + if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); + else TraceLog(LOG_WARNING, "Mesh tangents already exist"); - material.shader = GetShaderDefault(); - material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) - //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set - //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color - material.maps[MAP_SPECULAR].color = WHITE; // Specular color + for (int i = 0; i < mesh->vertexCount; i += 3) + { + // Get triangle vertices + Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; + Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; + Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; - return material; + // Get triangle texcoords + Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; + Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; + Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; + + float x1 = v2.x - v1.x; + float y1 = v2.y - v1.y; + float z1 = v2.z - v1.z; + float x2 = v3.x - v1.x; + float y2 = v3.y - v1.y; + float z2 = v3.z - v1.z; + + float s1 = uv2.x - uv1.x; + float t1 = uv2.y - uv1.y; + float s2 = uv3.x - uv1.x; + float t2 = uv3.y - uv1.y; + + float div = s1*t2 - s2*t1; + float r = (div == 0.0f)? 0.0f : 1.0f/div; + + Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; + Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; + + tan1[i + 0] = sdir; + tan1[i + 1] = sdir; + tan1[i + 2] = sdir; + + tan2[i + 0] = tdir; + tan2[i + 1] = tdir; + tan2[i + 2] = tdir; + } + + // Compute tangents considering normals + for (int i = 0; i < mesh->vertexCount; ++i) + { + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = tan1[i]; + + // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... + #if defined(COMPUTE_TANGENTS_METHOD_01) + Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); + tmp = Vector3Normalize(tmp); + mesh->tangents[i*4 + 0] = tmp.x; + mesh->tangents[i*4 + 1] = tmp.y; + mesh->tangents[i*4 + 2] = tmp.z; + mesh->tangents[i*4 + 3] = 1.0f; + #else + Vector3OrthoNormalize(&normal, &tangent); + mesh->tangents[i*4 + 0] = tangent.x; + mesh->tangents[i*4 + 1] = tangent.y; + mesh->tangents[i*4 + 2] = tangent.z; + mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; + #endif + } + + free(tan1); + free(tan2); + + TraceLog(LOG_INFO, "Tangents computed for mesh"); } -// Unload material from memory -void UnloadMaterial(Material material) +// Compute mesh binormals (aka bitangent) +void MeshBinormals(Mesh *mesh) { - // Unload material shader (avoid unloading default shader, managed by raylib) - if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); - - // Unload loaded texture maps (avoid unloading default texture, managed by raylib) - for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + for (int i = 0; i < mesh->vertexCount; i++) { - if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; + float tangentW = mesh->tangents[i*4 + 3]; + + // TODO: Register computed binormal in mesh->binormal? + // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); } } @@ -2239,129 +2696,6 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) return result; } -// Compute mesh bounding box limits -// NOTE: minVertex and maxVertex should be transformed by model transform matrix -BoundingBox MeshBoundingBox(Mesh mesh) -{ - // Get min and max vertex to construct bounds (AABB) - Vector3 minVertex = { 0 }; - Vector3 maxVertex = { 0 }; - - printf("Mesh vertex count: %i\n", mesh.vertexCount); - - if (mesh.vertices != NULL) - { - minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - - for (int i = 1; i < mesh.vertexCount; i++) - { - minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - } - } - - // Create the bounding box - BoundingBox box = { 0 }; - box.min = minVertex; - box.max = maxVertex; - - return box; -} - -// Compute mesh tangents -// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html -void MeshTangents(Mesh *mesh) -{ - if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); - else TraceLog(LOG_WARNING, "Mesh tangents already exist"); - - Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - - for (int i = 0; i < mesh->vertexCount; i += 3) - { - // Get triangle vertices - Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; - Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; - Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; - - // Get triangle texcoords - Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; - Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; - Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; - - float x1 = v2.x - v1.x; - float y1 = v2.y - v1.y; - float z1 = v2.z - v1.z; - float x2 = v3.x - v1.x; - float y2 = v3.y - v1.y; - float z2 = v3.z - v1.z; - - float s1 = uv2.x - uv1.x; - float t1 = uv2.y - uv1.y; - float s2 = uv3.x - uv1.x; - float t2 = uv3.y - uv1.y; - - float div = s1*t2 - s2*t1; - float r = (div == 0.0f)? 0.0f : 1.0f/div; - - Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; - Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; - - tan1[i + 0] = sdir; - tan1[i + 1] = sdir; - tan1[i + 2] = sdir; - - tan2[i + 0] = tdir; - tan2[i + 1] = tdir; - tan2[i + 2] = tdir; - } - - // Compute tangents considering normals - for (int i = 0; i < mesh->vertexCount; ++i) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = tan1[i]; - - // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... - #if defined(COMPUTE_TANGENTS_METHOD_01) - Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); - tmp = Vector3Normalize(tmp); - mesh->tangents[i*4 + 0] = tmp.x; - mesh->tangents[i*4 + 1] = tmp.y; - mesh->tangents[i*4 + 2] = tmp.z; - mesh->tangents[i*4 + 3] = 1.0f; - #else - Vector3OrthoNormalize(&normal, &tangent); - mesh->tangents[i*4 + 0] = tangent.x; - mesh->tangents[i*4 + 1] = tangent.y; - mesh->tangents[i*4 + 2] = tangent.z; - mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; - #endif - } - - free(tan1); - free(tan2); - - TraceLog(LOG_INFO, "Tangents computed for mesh"); -} - -// Compute mesh binormals (aka bitangent) -void MeshBinormals(Mesh *mesh) -{ - for (int i = 0; i < mesh->vertexCount; i++) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; - float tangentW = mesh->tangents[i*4 + 3]; - - // TODO: Register computed binormal in mesh->binormal? - // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); - } -} - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index c365fa47..55943baf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -752,7 +752,7 @@ typedef enum { MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_BRDF -} TexmapIndex; +} MaterialMapType; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS @@ -1256,16 +1256,25 @@ RLAPI void DrawGizmo(Vector3 position); // Model loading/unloading functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh -//RLAPI void LoadModelAnimations(const char fileName, ModelAnimation *anims, int *animsCount); // Load model animations from file -//RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) -// Mesh manipulation functions -RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents -RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +// Mesh loading/unloading functions +RLAPI Mesh *LoadMeshes(const char *fileName, int *meshCount); // Load meshes from model file RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file +RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) + +// Material loading/unloading functions +RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh + +// Model animations loading/unloading functions +RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animsCount); // Load model animations from file +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose +RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Mesh generation functions RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh @@ -1279,10 +1288,10 @@ RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data -// Material loading/unloading functions -RLAPI Material LoadMaterial(const char *fileName); // Load material from file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +// Mesh manipulation functions +RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents +RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals // Model drawing functions RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) -- cgit v1.2.3 From c600dd07668f301fc1a986326d807f341e6ecb78 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 16:43:09 +0200 Subject: Review PBR shaders Issue was related to vertex tangent attibutes not uploaded to GPU, a quick solution was implemented for new vertex attributes loading for already existing meshes... I don't like it specially but it will work for now. --- examples/models/models_material_pbr.c | 8 ++++++++ examples/models/resources/shaders/brdf.fs | 4 ++-- examples/models/resources/shaders/irradiance.fs | 4 ++-- examples/models/resources/shaders/prefilter.fs | 4 ++-- src/models.c | 3 +++ src/rlgl.h | 24 ++++++++++++++++++++++++ 6 files changed, 41 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index adb4762b..d393a20d 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -38,7 +38,11 @@ int main() // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); + + // Mesh tangents are generated... and uploaded to GPU + // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO MeshTangents(&model.meshes[0]); + model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); // Define lights attributes @@ -143,9 +147,13 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) #define PATH_BRDF_FS "resources/shaders/brdf.fs" // Path to bidirectional reflectance distribution function fragment shader Shader shdrCubemap = LoadShader(PATH_CUBEMAP_VS, PATH_CUBEMAP_FS); + printf("Loaded shader: cubemap\n"); Shader shdrIrradiance = LoadShader(PATH_SKYBOX_VS, PATH_IRRADIANCE_FS); + printf("Loaded shader: irradiance\n"); Shader shdrPrefilter = LoadShader(PATH_SKYBOX_VS, PATH_PREFILTER_FS); + printf("Loaded shader: prefilter\n"); Shader shdrBRDF = LoadShader(PATH_BRDF_VS, PATH_BRDF_FS); + printf("Loaded shader: brdf\n"); // Setup required shader locations SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, UNIFORM_INT); diff --git a/examples/models/resources/shaders/brdf.fs b/examples/models/resources/shaders/brdf.fs index 3e8777d2..d04bc661 100644 --- a/examples/models/resources/shaders/brdf.fs +++ b/examples/models/resources/shaders/brdf.fs @@ -10,13 +10,13 @@ #version 330 -#define MAX_SAMPLES 1024u // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; // Constant values const float PI = 3.14159265359; +const uint MAX_SAMPLES = 1024u; // Output fragment color out vec4 finalColor; @@ -93,7 +93,7 @@ vec2 IntegrateBRDF(float NdotV, float roughness) vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); vec3 N = vec3(0.0, 0.0, 1.0); - for (int i = 0; i < MAX_SAMPLES; i++) + for (uint i = 0u; i < MAX_SAMPLES; i++) { // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) diff --git a/examples/models/resources/shaders/irradiance.fs b/examples/models/resources/shaders/irradiance.fs index 87113673..b42d2143 100644 --- a/examples/models/resources/shaders/irradiance.fs +++ b/examples/models/resources/shaders/irradiance.fs @@ -9,7 +9,7 @@ #version 330 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -23,7 +23,7 @@ out vec4 finalColor; void main() { // The sample direction equals the hemisphere's orientation - vec3 normal = normalize(fragPos); + vec3 normal = normalize(fragPosition); vec3 irradiance = vec3(0.0); diff --git a/examples/models/resources/shaders/prefilter.fs b/examples/models/resources/shaders/prefilter.fs index f5cf64be..9439810d 100644 --- a/examples/models/resources/shaders/prefilter.fs +++ b/examples/models/resources/shaders/prefilter.fs @@ -11,7 +11,7 @@ #define CUBEMAP_RESOLUTION 1024.0 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -79,7 +79,7 @@ vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) void main() { // Make the simplyfying assumption that V equals R equals the normal - vec3 N = normalize(fragPos); + vec3 N = normalize(fragPosition); vec3 R = N; vec3 V = R; diff --git a/src/models.c b/src/models.c index e437a0ab..7a9acdf7 100644 --- a/src/models.c +++ b/src/models.c @@ -2302,6 +2302,9 @@ void MeshTangents(Mesh *mesh) free(tan1); free(tan2); + + // 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); TraceLog(LOG_INFO, "Tangents computed for mesh"); } diff --git a/src/rlgl.h b/src/rlgl.h index 3d433377..bf50ad0d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -456,6 +456,7 @@ void rlDeleteBuffers(unsigned int id); // Unload vertex data (V void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic); // Load a new attributes buffer //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -2532,6 +2533,29 @@ void rlLoadMesh(Mesh *mesh, bool dynamic) #endif } +// Load a new attributes buffer +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) glBindVertexArray(vaoId); + + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); + glVertexAttribPointer(shaderLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shaderLoc); + + if (vaoSupported) glBindVertexArray(0); +#endif + + return id; +} + // Update vertex data on GPU (upload new data to one buffer) void rlUpdateMesh(Mesh mesh, int buffer, int numVertex) { -- cgit v1.2.3 From 9282b8ba83909b353fd34a9f584597338c1928bd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 17:08:46 +0200 Subject: ADDED: SetShaderValueTexture() Some tweaks --- examples/models/models_material_pbr.c | 7 ++++++- src/raylib.h | 3 ++- src/rlgl.h | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index d393a20d..c1d43a24 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -34,7 +34,12 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [models] example - pbr material"); // Define the camera to look into our 3d world - Camera camera = {{ 4.0f, 4.0f, 4.0f }, { 0.0f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { 0 }; + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.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 // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); diff --git a/src/raylib.h b/src/raylib.h index 55943baf..dc864b8d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,7 +1331,8 @@ RLAPI Texture2D GetTextureDefault(void); // Get RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture); // Set shader uniform value for texture RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI Matrix GetMatrixModelview(); // Get internal modelview matrix diff --git a/src/rlgl.h b/src/rlgl.h index bf50ad0d..81f39d68 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -207,8 +207,8 @@ typedef unsigned char byte; // Animation vertex data float *animVertices; // Animated vertex positions (after bones transformations) float *animNormals; // Animated normals (after bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -3169,6 +3169,18 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) #endif } +// Set shader uniform value for texture +void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniform1i(uniformLoc, texture.id); + + //glUseProgram(0); +#endif +} + // Set a custom projection matrix (replaces internal projection matrix) void SetMatrixProjection(Matrix proj) { -- cgit v1.2.3 From c23ceec338ac46c28d0b1fe2ab286775efc7f041 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 17:29:30 +0200 Subject: Added missing include -_- --- examples/models/models_material_pbr.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'examples') diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index c1d43a24..5c308cfc 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -12,6 +12,8 @@ #include "raylib.h" #include "raymath.h" +#include + #define RLIGHTS_IMPLEMENTATION #include "rlights.h" -- cgit v1.2.3 From f21761fbbb02f0b58b5b54342f0c3ad3abc0003e Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Sun, 7 Apr 2019 17:49:12 +0200 Subject: Happy new year 2019 --- examples/Makefile | 2 +- examples/audio/audio_raw_stream.c | 2 +- examples/models/models_obj_viewer.c | 2 +- examples/others/rlgl_standalone.c | 2 +- games/Makefile | 2 +- games/cat_vs_roomba/Makefile | 2 +- games/cat_vs_roomba/roomba.c | 2 +- games/cat_vs_roomba/screens/screen_ending.c | 2 +- games/cat_vs_roomba/screens/screen_gameplay.c | 2 +- games/cat_vs_roomba/screens/screen_logo.c | 2 +- games/cat_vs_roomba/screens/screen_title.c | 2 +- games/cat_vs_roomba/screens/screens.h | 2 +- games/drturtle/Makefile | 2 +- games/just_do/Makefile | 2 +- games/koala_seasons/Makefile | 2 +- games/light_my_ritual/Makefile | 2 +- games/skully_escape/Makefile | 2 +- games/transmission/Makefile | 2 +- games/transmission/screens/screen_ending.c | 2 +- games/transmission/screens/screen_gameplay.c | 2 +- games/transmission/screens/screen_logo.c | 2 +- games/transmission/screens/screen_mission.c | 2 +- games/transmission/screens/screen_title.c | 2 +- games/transmission/screens/screens.h | 2 +- games/transmission/transmission.c | 2 +- games/wave_collector/Makefile | 2 +- projects/VSCode/Makefile | 2 +- src/Makefile | 2 +- src/core.c | 2 +- src/gestures.h | 2 +- src/models.c | 2 +- src/raylib.h | 2 +- src/rglfw.c | 2 +- src/rlgl.h | 2 +- src/shapes.c | 2 +- src/text.c | 2 +- src/textures.c | 2 +- src/utils.c | 2 +- src/utils.h | 2 +- templates/advance_game/Makefile | 2 +- templates/advance_game/advance_game.c | 2 +- templates/advance_game/screens/screen_ending.c | 2 +- templates/advance_game/screens/screen_gameplay.c | 2 +- templates/advance_game/screens/screen_logo.c | 2 +- templates/advance_game/screens/screen_options.c | 2 +- templates/advance_game/screens/screen_title.c | 2 +- templates/advance_game/screens/screens.h | 2 +- templates/simple_game/Makefile | 2 +- templates/simple_game/simple_game.c | 2 +- templates/standard_game/Makefile | 2 +- templates/standard_game/screens/screen_ending.c | 2 +- templates/standard_game/screens/screen_gameplay.c | 2 +- templates/standard_game/screens/screen_logo.c | 2 +- templates/standard_game/screens/screen_options.c | 2 +- templates/standard_game/screens/screen_title.c | 2 +- templates/standard_game/screens/screens.h | 2 +- templates/standard_game/standard_game.c | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile index e14762b3..b35e39f9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 7eee46f6..d7fa5d79 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -7,7 +7,7 @@ * This example has been created using raylib 1.6 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2015-2018 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2019 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index 0581df34..7d387441 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -5,7 +5,7 @@ * This example has been created using raylib 2.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 4b262bbd..42aec2e2 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -24,7 +24,7 @@ * This example is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/Makefile b/games/Makefile index 5d60f15d..44e053c6 100644 --- a/games/Makefile +++ b/games/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/Makefile b/games/cat_vs_roomba/Makefile index 6662a6be..b1304656 100644 --- a/games/cat_vs_roomba/Makefile +++ b/games/cat_vs_roomba/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/roomba.c b/games/cat_vs_roomba/roomba.c index 0d236775..eeee7a71 100644 --- a/games/cat_vs_roomba/roomba.c +++ b/games/cat_vs_roomba/roomba.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/cat_vs_roomba/screens/screen_ending.c b/games/cat_vs_roomba/screens/screen_ending.c index ef2a5f74..466d9b91 100644 --- a/games/cat_vs_roomba/screens/screen_ending.c +++ b/games/cat_vs_roomba/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/screens/screen_gameplay.c b/games/cat_vs_roomba/screens/screen_gameplay.c index 49a0bb6b..4dd13856 100644 --- a/games/cat_vs_roomba/screens/screen_gameplay.c +++ b/games/cat_vs_roomba/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/screens/screen_logo.c b/games/cat_vs_roomba/screens/screen_logo.c index 9fc704c7..a697013e 100644 --- a/games/cat_vs_roomba/screens/screen_logo.c +++ b/games/cat_vs_roomba/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/screens/screen_title.c b/games/cat_vs_roomba/screens/screen_title.c index 009fbd0a..6acadce5 100644 --- a/games/cat_vs_roomba/screens/screen_title.c +++ b/games/cat_vs_roomba/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/cat_vs_roomba/screens/screens.h b/games/cat_vs_roomba/screens/screens.h index 9cc07eab..0ad4f9af 100644 --- a/games/cat_vs_roomba/screens/screens.h +++ b/games/cat_vs_roomba/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/drturtle/Makefile b/games/drturtle/Makefile index 8f1934b5..4cd5033e 100644 --- a/games/drturtle/Makefile +++ b/games/drturtle/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/just_do/Makefile b/games/just_do/Makefile index b6c935f6..af9b31c1 100644 --- a/games/just_do/Makefile +++ b/games/just_do/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/koala_seasons/Makefile b/games/koala_seasons/Makefile index 8482bf2d..25dbe696 100644 --- a/games/koala_seasons/Makefile +++ b/games/koala_seasons/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/light_my_ritual/Makefile b/games/light_my_ritual/Makefile index fbc34aac..3291bb3e 100644 --- a/games/light_my_ritual/Makefile +++ b/games/light_my_ritual/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/skully_escape/Makefile b/games/skully_escape/Makefile index 8875ad52..4673a549 100644 --- a/games/skully_escape/Makefile +++ b/games/skully_escape/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/Makefile b/games/transmission/Makefile index a1b7e764..f62fe080 100644 --- a/games/transmission/Makefile +++ b/games/transmission/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index bb355b8b..0492a0cc 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index ee70632a..3dfce714 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screen_logo.c b/games/transmission/screens/screen_logo.c index 37543302..dc016423 100644 --- a/games/transmission/screens/screen_logo.c +++ b/games/transmission/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screen_mission.c b/games/transmission/screens/screen_mission.c index 1cd2563b..77777c73 100644 --- a/games/transmission/screens/screen_mission.c +++ b/games/transmission/screens/screen_mission.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screen_title.c b/games/transmission/screens/screen_title.c index 2a30a6ba..e9023b08 100644 --- a/games/transmission/screens/screen_title.c +++ b/games/transmission/screens/screen_title.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/screens/screens.h b/games/transmission/screens/screens.h index be5e31d9..49698f0d 100644 --- a/games/transmission/screens/screens.h +++ b/games/transmission/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/games/transmission/transmission.c b/games/transmission/transmission.c index 91ca28c1..9fc3d802 100644 --- a/games/transmission/transmission.c +++ b/games/transmission/transmission.c @@ -7,7 +7,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/wave_collector/Makefile b/games/wave_collector/Makefile index 06e74799..703383b8 100644 --- a/games/wave_collector/Makefile +++ b/games/wave_collector/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 747718fc..3cffaaba 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/src/Makefile b/src/Makefile index e2fe6c5d..0217c198 100644 --- a/src/Makefile +++ b/src/Makefile @@ -14,7 +14,7 @@ # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # -# Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. # In no event will the authors be held liable for any damages arising from diff --git a/src/core.c b/src/core.c index b0c0658d..42db214f 100644 --- a/src/core.c +++ b/src/core.c @@ -68,7 +68,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/gestures.h b/src/gestures.h index a4546eb1..36775333 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -24,7 +24,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/models.c b/src/models.c index 7a9acdf7..07f8203b 100644 --- a/src/models.c +++ b/src/models.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/raylib.h b/src/raylib.h index dc864b8d..dade7aba 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -49,7 +49,7 @@ * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rglfw.c b/src/rglfw.c index 853c5200..3463bb96 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -7,7 +7,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rlgl.h b/src/rlgl.h index 81f39d68..3e428241 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -42,7 +42,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/shapes.c b/src/shapes.c index b7f7e3df..fbc70fc4 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -14,7 +14,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/text.c b/src/text.c index d44cdd11..cd7a6802 100644 --- a/src/text.c +++ b/src/text.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/textures.c b/src/textures.c index cd0bd208..5189b635 100644 --- a/src/textures.c +++ b/src/textures.c @@ -38,7 +38,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/utils.c b/src/utils.c index 6b174354..10cce9b9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -11,7 +11,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/utils.h b/src/utils.h index 08b33962..d7ab8829 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 2e5d6e26..e475f72c 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/advance_game.c b/templates/advance_game/advance_game.c index 48a34f6d..3fb5d657 100644 --- a/templates/advance_game/advance_game.c +++ b/templates/advance_game/advance_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/advance_game/screens/screen_ending.c b/templates/advance_game/screens/screen_ending.c index 66b5ddf9..62d1267a 100644 --- a/templates/advance_game/screens/screen_ending.c +++ b/templates/advance_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/screens/screen_gameplay.c b/templates/advance_game/screens/screen_gameplay.c index 8943adb5..8ea61491 100644 --- a/templates/advance_game/screens/screen_gameplay.c +++ b/templates/advance_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/screens/screen_logo.c b/templates/advance_game/screens/screen_logo.c index 6282e83e..8badbf52 100644 --- a/templates/advance_game/screens/screen_logo.c +++ b/templates/advance_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/screens/screen_options.c b/templates/advance_game/screens/screen_options.c index dc8d74fa..4b58da13 100644 --- a/templates/advance_game/screens/screen_options.c +++ b/templates/advance_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/screens/screen_title.c b/templates/advance_game/screens/screen_title.c index 5727546a..8e74a027 100644 --- a/templates/advance_game/screens/screen_title.c +++ b/templates/advance_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/advance_game/screens/screens.h b/templates/advance_game/screens/screens.h index 4d7f9b53..7c2dfb48 100644 --- a/templates/advance_game/screens/screens.h +++ b/templates/advance_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index 86e0dbde..9f27a429 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/simple_game/simple_game.c b/templates/simple_game/simple_game.c index 028b1da8..8f2dc36a 100644 --- a/templates/simple_game/simple_game.c +++ b/templates/simple_game/simple_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/standard_game/Makefile b/templates/standard_game/Makefile index b9c6c188..4333c8b8 100644 --- a/templates/standard_game/Makefile +++ b/templates/standard_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screen_ending.c b/templates/standard_game/screens/screen_ending.c index 87196977..1bd5ce98 100644 --- a/templates/standard_game/screens/screen_ending.c +++ b/templates/standard_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screen_gameplay.c b/templates/standard_game/screens/screen_gameplay.c index 7f108265..427e5ba7 100644 --- a/templates/standard_game/screens/screen_gameplay.c +++ b/templates/standard_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screen_logo.c b/templates/standard_game/screens/screen_logo.c index c0b60571..435ea241 100644 --- a/templates/standard_game/screens/screen_logo.c +++ b/templates/standard_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screen_options.c b/templates/standard_game/screens/screen_options.c index 9f6690d1..df68dd26 100644 --- a/templates/standard_game/screens/screen_options.c +++ b/templates/standard_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screen_title.c b/templates/standard_game/screens/screen_title.c index 328448ba..5bd33825 100644 --- a/templates/standard_game/screens/screen_title.c +++ b/templates/standard_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/screens/screens.h b/templates/standard_game/screens/screens.h index e961b533..9450c29f 100644 --- a/templates/standard_game/screens/screens.h +++ b/templates/standard_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/templates/standard_game/standard_game.c b/templates/standard_game/standard_game.c index 8871484e..7918562c 100644 --- a/templates/standard_game/standard_game.c +++ b/templates/standard_game/standard_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ -- cgit v1.2.3 From 94335e30df6c66337eaad937048191c1735e6223 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 Apr 2019 12:50:09 +0200 Subject: Avoid example on Android --- examples/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4b04d45a..ab5ff548 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -47,6 +47,7 @@ if(${PLATFORM} MATCHES "Android") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_billboard.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_solar_system.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) -- cgit v1.2.3 From 5bfa67535040ed1cb717908d9f259db0c302d9e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 13:53:01 +0200 Subject: Review VR simulator mechanism - No default VR device parameteres inside raylib - VR device parameter should be provided by user - VR distortion shader should be provided by user --- examples/core/core_vr_simulator.c | 38 +++- examples/core/resources/distortion.fs | 43 ++++ src/config.h | 2 - src/raylib.h | 30 +-- src/rlgl.h | 414 ++++++++++------------------------ 5 files changed, 201 insertions(+), 326 deletions(-) create mode 100644 examples/core/resources/distortion.fs (limited to 'examples') diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 3f59e839..65054b73 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -23,7 +23,35 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); // Init VR simulator (Oculus Rift CV1 parameters) - InitVrSimulator(GetVrDeviceInfo(HMD_OCULUS_RIFT_CV1)); + InitVrSimulator(); + + VrDeviceInfo hmd = { 0 }; // VR device parameters (head-mounted-device) + + // Oculus Rift CV1 parameters for simulator + hmd.hResolution = 2160; // HMD horizontal resolution in pixels + hmd.vResolution = 1200; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters + hmd.vScreenSize = 0.0669f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters + + // NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders. + // Following parameters are an approximation to distortion stereo rendering but results differ from actual device. + hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + + // Distortion shader (uses device lens distortion and chroma) + Shader distortion = LoadShader(0, "resources/distortion.fs"); + + SetVrConfiguration(hmd, distortion); // Set Vr device parameters for stereo rendering // Define the camera to look into our 3d world Camera camera; @@ -77,9 +105,11 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - CloseVrSimulator(); // Close VR simulator - - CloseWindow(); // Close window and OpenGL context + UnloadShader(distortion); // Unload distortion shader + + CloseVrSimulator(); // Close VR simulator + + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/core/resources/distortion.fs b/examples/core/resources/distortion.fs new file mode 100644 index 00000000..207a0aff --- /dev/null +++ b/examples/core/resources/distortion.fs @@ -0,0 +1,43 @@ +#version 330 + +in vec2 fragTexCoord; +in vec4 fragColor; +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 leftLensCenter = vec2(0.288, 0.5); +uniform vec2 rightLensCenter = vec2(0.712, 0.5); +uniform vec2 leftScreenCenter = vec2(0.25, 0.5); +uniform vec2 rightScreenCenter = vec2(0.75, 0.5); +uniform vec2 scale = vec2(0.25, 0.45); +uniform vec2 scaleIn = vec2(4, 2.2222); +uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); +uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); + +void main() +{ + vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; + vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; + vec2 theta = (fragTexCoord - lensCenter)*scaleIn; + float rSq = theta.x*theta.x + theta.y*theta.y; + vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); + vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); + vec2 tcBlue = lensCenter + scale*thetaBlue; + + if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) + { + finalColor = vec4(0.0, 0.0, 0.0, 1.0); + } + else + { + float blue = texture(texture0, tcBlue).b; + vec2 tcGreen = lensCenter + scale*theta1; + float green = texture(texture0, tcGreen).g; + + vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); + vec2 tcRed = lensCenter + scale*thetaRed; + + float red = texture(texture0, tcRed).r; + finalColor = vec4(red, green, blue, 1.0); + } +}; diff --git a/src/config.h b/src/config.h index dc30eeb2..2ea4b438 100644 --- a/src/config.h +++ b/src/config.h @@ -57,8 +57,6 @@ //------------------------------------------------------------------------------------ // Support VR simulation functionality (stereo rendering) #define SUPPORT_VR_SIMULATOR 1 -// Include stereo rendering distortion shader (shader_distortion.h) -#define SUPPORT_DISTORTION_SHADER 1 //------------------------------------------------------------------------------------ diff --git a/src/raylib.h b/src/raylib.h index e8d927cb..73661f58 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -443,16 +443,6 @@ typedef struct VrDeviceInfo { float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; -// VR Stereo rendering configuration for simulator -typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer - Shader distortionShader; // VR stereo rendering distortion shader - Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices - Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices - int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] - int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] -} VrStereoConfig; - //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -860,16 +850,6 @@ typedef enum { CAMERA_ORTHOGRAPHIC } CameraType; -// Head Mounted Display devices -typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR -} VrDeviceType; - // Type of n-patch typedef enum { NPT_9PATCH = 0, // Npatch defined by 3x3 tiles @@ -1330,7 +1310,7 @@ RLAPI Shader GetShaderDefault(void); // Get RLAPI Texture2D GetTextureDefault(void); // Get default texture // Shader configuration functions -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) @@ -1344,7 +1324,7 @@ RLAPI Matrix GetMatrixModelview(); // Get RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture // Shading begin/end functions RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing @@ -1355,10 +1335,10 @@ RLAPI void BeginScissorMode(int x, int y, int width, int height); // Beg RLAPI void EndScissorMode(void); // End scissor mode // VR control functions -RLAPI VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -RLAPI void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready RLAPI void ToggleVrMode(void); // Enable/Disable VR experience RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering diff --git a/src/rlgl.h b/src/rlgl.h index 3e428241..5c0d2823 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -32,9 +32,6 @@ * #define SUPPORT_VR_SIMULATOR * Support VR simulation functionality (stereo rendering) * -* #define SUPPORT_DISTORTION_SHADER -* Include stereo rendering distortion shader (embedded) -* * DEPENDENCIES: * raymath - 3D math functionality (Vector3, Matrix, Quaternion) * GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) @@ -263,7 +260,6 @@ typedef unsigned char byte; // VR Stereo rendering configuration for simulator typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer Shader distortionShader; // VR stereo rendering distortion shader Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices @@ -390,16 +386,6 @@ typedef unsigned char byte; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS - - // VR Head Mounted Display devices - typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR - } VrDevice; #endif #if defined(__cplusplus) @@ -534,14 +520,14 @@ void BeginBlendMode(int mode); // Begin blending mode (alpha, void EndBlendMode(void); // End blending mode (reset to default: alpha blending) // VR control functions -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -void CloseVrSimulator(void); // Close VR simulator for current device -bool IsVrSimulatorReady(void); // Detect if VR simulator is ready -void ToggleVrMode(void); // Enable/Disable VR experience -void BeginVrDrawing(void); // Begin VR simulator stereo rendering -void EndVrDrawing(void); // End VR simulator stereo rendering +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters +RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters +RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready +RLAPI void ToggleVrMode(void); // Enable/Disable VR experience +RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering +RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) @@ -561,10 +547,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #if defined(RLGL_IMPLEMENTATION) -#if defined(RLGL_STANDALONE) - #define SUPPORT_VR_SIMULATOR - #define SUPPORT_DISTORTION_SHADER -#else +#if !defined(RLGL_STANDALONE) // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags @@ -740,88 +723,20 @@ typedef struct DrawCall { //Matrix modelview; // Modelview matrix for this draw } DrawCall; +#if defined(SUPPORT_VR_SIMULATOR) +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Shader distortionShader; // VR stereo rendering distortion shader + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] +} VrStereoConfig; +#endif + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER) - // Distortion shader embedded - static char distortionFShaderStr[] = - #if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" - #elif defined(GRAPHICS_API_OPENGL_ES2) - "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - #endif - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragColor; \n" - "out vec4 finalColor; \n" - #endif - "uniform sampler2D texture0; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "uniform vec2 leftLensCenter; \n" - "uniform vec2 rightLensCenter; \n" - "uniform vec2 leftScreenCenter; \n" - "uniform vec2 rightScreenCenter; \n" - "uniform vec2 scale; \n" - "uniform vec2 scaleIn; \n" - "uniform vec4 hmdWarpParam; \n" - "uniform vec4 chromaAbParam; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "uniform vec2 leftLensCenter = vec2(0.288, 0.5); \n" - "uniform vec2 rightLensCenter = vec2(0.712, 0.5); \n" - "uniform vec2 leftScreenCenter = vec2(0.25, 0.5); \n" - "uniform vec2 rightScreenCenter = vec2(0.75, 0.5); \n" - "uniform vec2 scale = vec2(0.25, 0.45); \n" - "uniform vec2 scaleIn = vec2(4, 2.2222); \n" - "uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); \n" - "uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); \n" - #endif - "void main() \n" - "{ \n" - " vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; \n" - " vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; \n" - " vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n" - " float rSq = theta.x*theta.x + theta.y*theta.y; \n" - " vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n" - " vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); \n" - " vec2 tcBlue = lensCenter + scale*thetaBlue; \n" - " if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " finalColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #endif - " } \n" - " else \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float blue = texture2D(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture2D(texture0, tcGreen).g; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float blue = texture(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture(texture0, tcGreen).g; \n" - #endif - " vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); \n" - " vec2 tcRed = lensCenter + scale*thetaRed; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float red = texture2D(texture0, tcRed).r; \n" - " gl_FragColor = vec4(red, green, blue, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float red = texture(texture0, tcRed).r; \n" - " finalColor = vec4(red, green, blue, 1.0); \n" - #endif - " } \n" - "} \n"; -#endif - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static Matrix stack[MAX_MATRIX_STACK_SIZE] = { 0 }; static int stackCounter = 0; @@ -891,6 +806,7 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; #if defined(SUPPORT_VR_SIMULATOR) // VR global variables static VrStereoConfig vrConfig = { 0 }; // VR stereo configuration for simulator +static RenderTexture2D stereoFbo; // VR stereo rendering framebuffer static bool vrSimulatorReady = false; // VR simulator ready flag static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag // NOTE: This flag is useful to render data over stereo image (i.e. FPS) @@ -924,7 +840,6 @@ static void GenDrawCube(void); // Generate and draw cube static void GenDrawQuad(void); // Generate and draw quad #if defined(SUPPORT_VR_SIMULATOR) -static VrStereoConfig SetStereoConfig(VrDeviceInfo info, Shader distortion); // Configure stereo rendering (including distortion shader) with HMD device parameters static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); // Set internal projection and modelview matrix depending on eye #endif @@ -3577,101 +3492,17 @@ void EndScissorMode(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Get VR device information for some standard devices -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType) -{ - VrDeviceInfo hmd = { 0 }; // Current VR device info - - switch (vrDeviceType) - { - case HMD_DEFAULT_DEVICE: - case HMD_OCULUS_RIFT_CV1: - { - // Oculus Rift CV1 parameters - // NOTE: CV1 represents a complete HMD redesign compared to previous versions, - // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, - // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. - // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering - // but result is not the same obtained with Oculus PC SDK. - hmd.hResolution = 2160; // HMD horizontal resolution in pixels - hmd.vResolution = 1200; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters - hmd.vScreenSize = 0.0669f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift CV1)"); - } break; - case HMD_OCULUS_RIFT_DK2: - { - // Oculus Rift DK2 parameters - hmd.hResolution = 1280; // HMD horizontal resolution in pixels - hmd.vResolution = 800; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.14976f; // HMD horizontal size in meters - hmd.vScreenSize = 0.09356f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift DK2)"); - } break; - case HMD_OCULUS_GO: - { - // TODO: Provide device display and lens parameters - } - case HMD_VALVE_HTC_VIVE: - { - // TODO: Provide device display and lens parameters - } - case HMD_SONY_PSVR: - { - // TODO: Provide device display and lens parameters - } - default: break; - } - - return hmd; -} - // Init VR simulator for selected device parameters -// NOTE: It modifies the global variable: VrStereoConfig vrConfig -void InitVrSimulator(VrDeviceInfo info) +// NOTE: It modifies the global variable: stereoFbo +void InitVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - Shader distortion = { 0 }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Load distortion shader - distortion = LoadShaderCode(NULL, distortionFShaderStr); - if (distortion.id > 0) SetShaderDefaultLocations(&distortion); -#endif - - // Set VR configutarion parameters, including distortion shader - vrConfig = SetStereoConfig(info, distortion); - + // Initialize framebuffer and textures for stereo rendering + // NOTE: Screen size should match HMD aspect ratio + stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); + vrSimulatorReady = true; -#endif - -#if defined(GRAPHICS_API_OPENGL_11) +#else TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); #endif } @@ -3687,14 +3518,90 @@ void UpdateVrTracking(Camera *camera) void CloseVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vrSimulatorReady) + if (vrSimulatorReady) rlDeleteRenderTextures(stereoFbo); // Unload stereo framebuffer and texture +#endif +} + +// Set stereo rendering configuration parameters +void SetVrConfiguration(VrDeviceInfo hmd, Shader distortion) +{ + // Reset vrConfig for a new values assignment + memset(&vrConfig, 0, sizeof(vrConfig)); + + // Assign distortion shader + vrConfig.distortionShader = distortion; + + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.lensDistortionValues[0] + + hmd.lensDistortionValues[1]*lensRadiusSq + + hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + + hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + vrConfig.eyeViewportRight[2] = hmd.hResolution/2; + vrConfig.eyeViewportRight[3] = hmd.vResolution; + + vrConfig.eyeViewportLeft[0] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[1] = 0; + vrConfig.eyeViewportLeft[2] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[3] = hmd.vResolution; + + if (vrConfig.distortionShader.id > 0) { - rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture - #if defined(SUPPORT_DISTORTION_SHADER) - UnloadShader(vrConfig.distortionShader); // Unload distortion shader - #endif + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); } -#endif } // Detect if VR simulator is running @@ -3733,7 +3640,7 @@ void BeginVrDrawing(void) if (vrSimulatorReady) { // Setup framebuffer for stereo rendering - rlEnableRenderTexture(vrConfig.stereoFbo.id); + rlEnableRenderTexture(stereoFbo.id); // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: @@ -3771,13 +3678,11 @@ void EndVrDrawing(void) rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix rlLoadIdentity(); // Reset internal modelview matrix -#if defined(SUPPORT_DISTORTION_SHADER) - // Draw RenderTexture (stereoFbo) using distortion shader - currentShader = vrConfig.distortionShader; -#else - currentShader = GetShaderDefault(); -#endif - rlEnableTexture(vrConfig.stereoFbo.texture.id); + // Draw RenderTexture (stereoFbo) using distortion shader if available + if (vrConfig.distortionShader.id > 0) currentShader = vrConfig.distortionShader; + else currentShader = GetShaderDefault(); + + rlEnableTexture(stereoFbo.texture.id); rlPushMatrix(); rlBegin(RL_QUADS); @@ -3790,15 +3695,15 @@ void EndVrDrawing(void) // Bottom-right corner for texture and quad rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(0.0f, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f(0.0f, (float)stereoFbo.texture.height); // Top-right corner for texture and quad rlTexCoord2f(1.0f, 0.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f( (float)stereoFbo.texture.width, (float)stereoFbo.texture.height); // Top-left corner for texture and quad rlTexCoord2f(1.0f, 1.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, 0.0f); + rlVertex2f( (float)stereoFbo.texture.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -4472,87 +4377,6 @@ static void GenDrawCube(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Configure stereo rendering (including distortion shader) with HMD device parameters -static VrStereoConfig SetStereoConfig(VrDeviceInfo hmd, Shader distortion) -{ - VrStereoConfig config = { 0 }; - - // Initialize framebuffer and textures for stereo rendering - // NOTE: Screen size should match HMD aspect ratio - config.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); - - // Assign distortion shader - config.distortionShader = distortion; - - // Compute aspect ratio - float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; - - // Compute lens parameters - float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; - float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; - float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; - float leftScreenCenter[2] = { 0.25f, 0.5f }; - float rightScreenCenter[2] = { 0.75f, 0.5f }; - - // Compute distortion scale parameters - // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] - float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); - float lensRadiusSq = lensRadius*lensRadius; - float distortionScale = hmd.lensDistortionValues[0] + - hmd.lensDistortionValues[1]*lensRadiusSq + - hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + - hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; - - TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); - - float normScreenWidth = 0.5f; - float normScreenHeight = 1.0f; - float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; - float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; - - TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); - - // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) - // ...but with lens distortion it is increased (see Oculus SDK Documentation) - //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? - float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); - - // Compute camera projection matrices - float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] - Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); - config.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); - config.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); - - // Compute camera transformation matrices - // NOTE: Camera movement might seem more natural if we model the head. - // Our axis of rotation is the base of our head, so we might want to add - // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. - config.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - config.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - - // Compute eyes Viewports - //config.eyeViewportRight[0] = (int[4]){ 0, 0, hmd.hResolution/2, hmd.vResolution }; - //config.eyeViewportLeft[0] = (int[4]){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Update distortion shader with lens and distortion-scale parameters - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); - - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scale"), scale, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); -#endif - - return config; -} - // Set internal projection and modelview matrix depending on eyes tracking data static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) { -- cgit v1.2.3 From 6fc97643bf564ec2f616ba3114230c78ed1c265e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 16:53:02 +0200 Subject: new example: textures_background_scrolling --- .../resources/cyberpunk_street_background.png | Bin 0 -> 7800 bytes .../resources/cyberpunk_street_foreground.png | Bin 0 -> 18100 bytes .../resources/cyberpunk_street_midground.png | Bin 0 -> 7867 bytes examples/textures/textures_background_scrolling.c | 87 +++++++++++++++++++++ .../textures/textures_background_scrolling.png | Bin 0 -> 42935 bytes 5 files changed, 87 insertions(+) create mode 100644 examples/textures/resources/cyberpunk_street_background.png create mode 100644 examples/textures/resources/cyberpunk_street_foreground.png create mode 100644 examples/textures/resources/cyberpunk_street_midground.png create mode 100644 examples/textures/textures_background_scrolling.c create mode 100644 examples/textures/textures_background_scrolling.png (limited to 'examples') diff --git a/examples/textures/resources/cyberpunk_street_background.png b/examples/textures/resources/cyberpunk_street_background.png new file mode 100644 index 00000000..624a4b14 Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_background.png differ diff --git a/examples/textures/resources/cyberpunk_street_foreground.png b/examples/textures/resources/cyberpunk_street_foreground.png new file mode 100644 index 00000000..fb622db6 Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_foreground.png differ diff --git a/examples/textures/resources/cyberpunk_street_midground.png b/examples/textures/resources/cyberpunk_street_midground.png new file mode 100644 index 00000000..643a85e7 Binary files /dev/null and b/examples/textures/resources/cyberpunk_street_midground.png differ diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c new file mode 100644 index 00000000..2be0810a --- /dev/null +++ b/examples/textures/textures_background_scrolling.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [textures] example - Background scrolling +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - background scrolling"); + + // NOTE: Be careful, background width must be equal or bigger than screen width + // if not, texture should be draw more than two times for scrolling effect + Texture2D background = LoadTexture("resources/cyberpunk_street_background.png"); + Texture2D midground = LoadTexture("resources/cyberpunk_street_midground.png"); + Texture2D foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); + + float scrollingBack = 0; + float scrollingMid = 0; + float scrollingFore = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + scrollingBack -= 0.1f; + scrollingMid -= 0.5f; + scrollingFore -= 1.0f; + + // NOTE: Texture is scaled twice its size, so it sould be considered on scrolling + if (scrollingBack <= -background.width*2) scrollingBack = 0; + if (scrollingMid <= -midground.width*2) scrollingMid = 0; + if (scrollingFore <= -foreground.width*2) scrollingFore = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GetColor(0x052c46ff)); + + // Draw background image twice + // NOTE: Texture is scaled twice its size + DrawTextureEx(background, (Vector2){ scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(background, (Vector2){ background.width*2 + scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + + // Draw midground image twice + DrawTextureEx(midground, (Vector2){ scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(midground, (Vector2){ midground.width*2 + scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + + // Draw foreground image twice + DrawTextureEx(foreground, (Vector2){ scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(foreground, (Vector2){ foreground.width*2 + scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + + DrawText("BACKGROUND SCROLLING & PARALLAX", 10, 10, 20, RED); + DrawText("(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)", screenWidth - 330, screenHeight - 20, 10, RAYWHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(background); // Unload background texture + UnloadTexture(midground); // Unload midground texture + UnloadTexture(foreground); // Unload foreground texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_background_scrolling.png b/examples/textures/textures_background_scrolling.png new file mode 100644 index 00000000..d21e269d Binary files /dev/null and b/examples/textures/textures_background_scrolling.png differ -- cgit v1.2.3 From 129703fad18b478d015a520524d46ab4afa2cb79 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 16:53:20 +0200 Subject: new example: shaders_texture_drawing --- .../resources/shaders/glsl330/cubes_panning.fs | 61 ++++++++++++++++++ examples/shaders/shaders_texture_drawing.c | 71 +++++++++++++++++++++ examples/shaders/shaders_texture_drawing.png | Bin 0 -> 16865 bytes 3 files changed, 132 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl330/cubes_panning.fs create mode 100644 examples/shaders/shaders_texture_drawing.c create mode 100644 examples/shaders/shaders_texture_drawing.png (limited to 'examples') diff --git a/examples/shaders/resources/shaders/glsl330/cubes_panning.fs b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs new file mode 100644 index 00000000..c92418a4 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs @@ -0,0 +1,61 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Output fragment color +out vec4 finalColor; + +// Custom variables +#define PI 3.14159265358979323846 +uniform float uTime = 0.0; + +float divisions = 5.0; +float angle = 0.0; + +vec2 VectorRotateTime(vec2 v, float speed) +{ + float time = uTime*speed; + float localTime = fract(time); // The time domain this works on is 1 sec. + + if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; + else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4*sin(2*PI*localTime - PI/2); + else if ((localTime >= 0.50) && (localTime < 0.75)) angle = PI*0.25; + else if ((localTime >= 0.75) && (localTime < 1.00)) angle = PI/4*sin(2*PI*localTime); + + // Rotate vector by angle + v -= 0.5; + v = mat2(cos(angle), -sin(angle), sin(angle), cos(angle))*v; + v += 0.5; + + return v; +} + +float Rectangle(in vec2 st, in float size, in float fill) +{ + float roundSize = 0.5 - size/2.0; + float left = step(roundSize, st.x); + float top = step(roundSize, st.y); + float bottom = step(roundSize, 1.0 - st.y); + float right = step(roundSize, 1.0 - st.x); + + return (left*bottom*right*top)*fill; +} + +void main() +{ + vec2 fragPos = fragTexCoord; + fragPos.xy += uTime/9.0; + + fragPos *= divisions; + vec2 ipos = floor(fragPos); // Get the integer coords + vec2 fpos = fract(fragPos); // Get the fractional coords + + fpos = VectorRotateTime(fpos, 0.2); + + float alpha = Rectangle(fpos, 0.216, 1.0); + vec3 color = vec3(0.3, 0.3, 0.3); + + finalColor = vec4(color, alpha); +} \ No newline at end of file diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c new file mode 100644 index 00000000..cb8a9c1e --- /dev/null +++ b/examples/shaders/shaders_texture_drawing.c @@ -0,0 +1,71 @@ +/******************************************************************************************* +* +* raylib [textures] example - Shader texture drawing +* +* This example illustrates how to draw on a blank texture using a shader +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Michał Ciesielski and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shader texture drawing"); + + Image imBlank = GenImageColor(1024, 1024, BLANK); + Texture2D texture = LoadTextureFromImage(imBlank); // Load blank texture to fill on shader + UnloadImage(imBlank); + + // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + Shader shader = LoadShader(0, "resources/shaders/glsl330/cubes_panning.fs"); + + float time = 0.0f; + int timeLoc = GetShaderLocation(shader, "uTime"); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + time = GetTime(); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginShaderMode(shader); // Enable our custom shader for next shapes/textures drawings + DrawTexture(texture, 0, 0, WHITE); // Drawing BLANK texture, all magic happens on shader + EndShaderMode(); // Disable our custom shader, return to default shader + + DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, MAROON); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shader); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_texture_drawing.png b/examples/shaders/shaders_texture_drawing.png new file mode 100644 index 00000000..12df6fae Binary files /dev/null and b/examples/shaders/shaders_texture_drawing.png differ -- cgit v1.2.3 From 24f07aaf5a58aa1fc1d18a9eae4327f5a3f58014 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 17:40:13 +0200 Subject: new example: core_window_scale_letterbox --- examples/core/core_window_scale_letterbox.c | 88 ++++++++++++++++++++++++++ examples/core/core_window_scale_letterbox.png | Bin 0 -> 17967 bytes 2 files changed, 88 insertions(+) create mode 100644 examples/core/core_window_scale_letterbox.c create mode 100644 examples/core/core_window_scale_letterbox.png (limited to 'examples') diff --git a/examples/core/core_window_scale_letterbox.c b/examples/core/core_window_scale_letterbox.c new file mode 100644 index 00000000..ea4a375a --- /dev/null +++ b/examples/core/core_window_scale_letterbox.c @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* raylib [core] example - window scale letterbox +* +* 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) +* +* Copyright (c) 2019 Anata and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define max(a, b) ((a)>(b)? (a) : (b)) +#define min(a, b) ((a)<(b)? (a) : (b)) + +int main() +{ + const int windowWidth = 800; + const int windowHeight = 450; + + // Enable config flags for resizable window and vertical synchro + SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); + InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox"); + SetWindowMinSize(320, 240); + + int gameScreenWidth = 640; + int gameScreenHeight = 480; + + // Render texture initialization + RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight); + SetTextureFilter(target.texture, FILTER_BILINEAR); // Texture scale filter to use + + Color colors[10] = { 0 }; + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while( !WindowShouldClose() ) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Compute required framebuffer scaling + float scale = min((float)GetScreenWidth()/gameScreenWidth, (float)GetScreenHeight()/gameScreenHeight); + + if (IsKeyPressed(KEY_SPACE)) + { + // Recalculate random colors for the bars + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(BLACK); + + // Draw everything in the render texture + BeginTextureMode(target); + + ClearBackground(RAYWHITE); // Clear render texture background color + + for (int i = 0; i < 10; i++) DrawRectangle(0, (gameScreenHeight/10)*i, gameScreenWidth, gameScreenHeight/10, colors[i]); + + DrawText("You can resize the window,\nand see the screen scaling!", 10, 25, 20, WHITE); + + EndTextureMode(); + + // Draw RenderTexture2D to window, properly scaled + DrawTexturePro(target.texture, (Rectangle){ 0.0f, 0.0f, (float)target.texture.width, (float)-target.texture.height }, + (Rectangle){ (GetScreenWidth() - ((float)gameScreenWidth*scale))*0.5, (GetScreenHeight() - ((float)gameScreenHeight*scale))*0.5, + (float)gameScreenWidth*scale, (float)gameScreenHeight*scale }, (Vector2){ 0, 0 }, 0.0f, WHITE); + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_window_scale_letterbox.png b/examples/core/core_window_scale_letterbox.png new file mode 100644 index 00000000..5acf2d7c Binary files /dev/null and b/examples/core/core_window_scale_letterbox.png differ -- cgit v1.2.3 From e54a74f4e7842b0e33f8cf8f7d78e58e297e1cb0 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 18:07:20 +0200 Subject: new example: shapes_rectangle_scaling_mouse --- examples/shapes/shapes_rectangle_scaling_mouse.c | 92 +++++++++++++++++++++ examples/shapes/shapes_rectangle_scaling_mouse.png | Bin 0 -> 15191 bytes 2 files changed, 92 insertions(+) create mode 100644 examples/shapes/shapes_rectangle_scaling_mouse.c create mode 100644 examples/shapes/shapes_rectangle_scaling_mouse.png (limited to 'examples') diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.c b/examples/shapes/shapes_rectangle_scaling_mouse.c new file mode 100644 index 00000000..d8c521ca --- /dev/null +++ b/examples/shapes/shapes_rectangle_scaling_mouse.c @@ -0,0 +1,92 @@ +/******************************************************************************************* +* +* raylib [shapes] example - rectangle scaling by mouse +* +* 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) +* +* Copyright (c) 2019 Demioz and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MOUSE_SCALE_MARK_SIZE 12 + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse"); + + Rectangle rec = { 100, 100, 200, 80 }; + + Vector2 mousePosition = { 0 }; + + bool mouseScaleReady = false; + bool mouseScaleMode = false; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + if (CheckCollisionPointRec(mousePosition, rec) && + CheckCollisionPointRec(mousePosition, (Rectangle){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE })) + { + mouseScaleReady = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) mouseScaleMode = true; + } + else mouseScaleReady = false; + + if (mouseScaleMode) + { + mouseScaleReady = true; + + rec.width = (mousePosition.x - rec.x); + rec.height = (mousePosition.y - rec.y); + + if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE; + if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_SIZE; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) mouseScaleMode = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10, 20, GRAY); + + DrawRectangleRec(rec, Fade(GREEN, 0.5f)); + + if (mouseScaleReady) + { + DrawRectangleLinesEx(rec, 1, RED); + DrawTriangle((Vector2){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE }, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.png b/examples/shapes/shapes_rectangle_scaling_mouse.png new file mode 100644 index 00000000..83d67de9 Binary files /dev/null and b/examples/shapes/shapes_rectangle_scaling_mouse.png differ -- cgit v1.2.3 From 8774cb3c242373a2eb9f62af88ff9b51b6a17d9a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 23:38:13 +0200 Subject: Removed iqm example Already integrated into raylib --- .../others/iqm_loader/models_iqm_animation.gif | Bin 593783 -> 0 bytes examples/others/iqm_loader/raymath.h | 1334 -------------------- examples/others/iqm_loader/riqm.h | 98 -- 3 files changed, 1432 deletions(-) delete mode 100644 examples/others/iqm_loader/models_iqm_animation.gif delete mode 100644 examples/others/iqm_loader/raymath.h delete mode 100644 examples/others/iqm_loader/riqm.h (limited to 'examples') diff --git a/examples/others/iqm_loader/models_iqm_animation.gif b/examples/others/iqm_loader/models_iqm_animation.gif deleted file mode 100644 index 3a51ad85..00000000 Binary files a/examples/others/iqm_loader/models_iqm_animation.gif and /dev/null differ diff --git a/examples/others/iqm_loader/raymath.h b/examples/others/iqm_loader/raymath.h deleted file mode 100644 index a574dd0d..00000000 --- a/examples/others/iqm_loader/raymath.h +++ /dev/null @@ -1,1334 +0,0 @@ -/********************************************************************************************** -* -* raymath v1.2 - Math functions to work with Vector3, Matrix and Quaternions -* -* CONFIGURATION: -* -* #define RAYMATH_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RAYMATH_HEADER_ONLY -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* -* #define RAYMATH_STANDALONE -* Avoid raylib.h header inclusion in this file. -* Vector3 and Matrix data types are defined internally in raymath module. -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2015-2017 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RAYMATH_H -#define RAYMATH_H - -//#define RAYMATH_STANDALONE // NOTE: To use raymath as standalone lib, just uncomment this line -//#define RAYMATH_HEADER_ONLY // NOTE: To compile functions as static inline, uncomment this line - -#ifndef RAYMATH_STANDALONE - #include "raylib.h" // Required for structs: Vector3, Matrix -#endif - -#ifdef __cplusplus - #define RMEXTERN extern "C" // Functions visible from other files (no name mangling of functions in C++) -#else - #define RMEXTERN // Functions visible from other files -#endif - -#if defined RAYMATH_IMPLEMENTATION && defined RAYMATH_HEADER_ONLY - #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_HEADER_ONLY is contradictory" -#endif - -#ifdef RAYMATH_IMPLEMENTATION - #define RMDEF extern inline // Provide external definition -#elif defined RAYMATH_HEADER_ONLY - #define RMDEF static inline // Functions may be inlined, no external out-of-line definition -#else - #ifdef __TINYC__ - #define RMDEF static inline // plain inline not supported by tinycc (See issue #435) - #else - #define RMDEF inline // Functions may be inlined or external definition used - #endif -#endif - - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846 -#endif - -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif - -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Return float vector for Matrix -#ifndef MatrixToFloat - #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) -#endif - -// Return float vector for Vector3 -#ifndef Vector3ToFloat - #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -#if defined(RAYMATH_STANDALONE) - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; - - // Vector3 type - typedef struct Vector3 { - float x; - float y; - float z; - } Vector3; - - // Matrix type (OpenGL style 4x4 - right handed, column major) - typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; - } Matrix; - - // Quaternion type - typedef struct Quaternion { - float x; - float y; - float z; - float w; - } Quaternion; -#endif - -// NOTE: Helper types to be used instead of array return types for *ToFloat functions -typedef struct float3 { float v[3]; } float3; -typedef struct float16 { float v[16]; } float16; - -#include // Required for: sinf(), cosf(), tan(), fabs() - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Utils math -//---------------------------------------------------------------------------------- - -// Clamp float value -RMDEF float Clamp(float value, float min, float max) -{ - const float res = value < min ? min : value; - return res > max ? max : res; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector2 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector2 Vector2Zero(void) -{ - Vector2 result = { 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector2 Vector2One(void) -{ - Vector2 result = { 1.0f, 1.0f }; - return result; -} - -// Add two vectors (v1 + v2) -RMDEF Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - return result; -} - -// Subtract two vectors (v1 - v2) -RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - return result; -} - -// Calculate vector length -RMDEF float Vector2Length(Vector2 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y)); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector2Distance(Vector2 v1, Vector2 v2) -{ - float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - return result; -} - -// Calculate angle from two vectors in X-axis -RMDEF float Vector2Angle(Vector2 v1, Vector2 v2) -{ - float result = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); - if (result < 0) result += 360.0f; - return result; -} - -// Scale vector (multiply by value) -RMDEF Vector2 Vector2Scale(Vector2 v, float scale) -{ - Vector2 result = { v.x*scale, v.y*scale }; - return result; -} - -// Negate vector -RMDEF Vector2 Vector2Negate(Vector2 v) -{ - Vector2 result = { -v.x, -v.y }; - return result; -} - -// Divide vector by a float value -RMDEF Vector2 Vector2Divide(Vector2 v, float div) -{ - Vector2 result = { v.x/div, v.y/div }; - return result; -} - -// Normalize provided vector -RMDEF Vector2 Vector2Normalize(Vector2 v) -{ - Vector2 result = Vector2Divide(v, Vector2Length(v)); - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector3 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector3 Vector3Zero(void) -{ - Vector3 result = { 0.0f, 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector3 Vector3One(void) -{ - Vector3 result = { 1.0f, 1.0f, 1.0f }; - return result; -} - -// Add two vectors -RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - return result; -} - -// Substract two vectors -RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - return result; -} - -// Multiply vector by scalar -RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar) -{ - Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; - return result; -} - -// Multiply vector by vector -RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; - return result; -} - -// Calculate two vectors cross product -RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; - return result; -} - -// Calculate one vector perpendicular vector -RMDEF Vector3 Vector3Perpendicular(Vector3 v) -{ - Vector3 result = { 0 }; - - float min = fabs(v.x); - Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - - if (fabs(v.y) < min) - { - min = fabs(v.y); - Vector3 tmp = {0.0f, 1.0f, 0.0f}; - cardinalAxis = tmp; - } - - if (fabs(v.z) < min) - { - Vector3 tmp = {0.0f, 0.0f, 1.0f}; - cardinalAxis = tmp; - } - - result = Vector3CrossProduct(v, cardinalAxis); - - return result; -} - -// Calculate vector length -RMDEF float Vector3Length(const Vector3 v) -{ - float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector3Distance(Vector3 v1, Vector3 v2) -{ - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - float result = sqrtf(dx*dx + dy*dy + dz*dz); - return result; -} - -// Scale provided vector -RMDEF Vector3 Vector3Scale(Vector3 v, float scale) -{ - Vector3 result = { v.x*scale, v.y*scale, v.z*scale }; - return result; -} - -// Negate provided vector (invert direction) -RMDEF Vector3 Vector3Negate(Vector3 v) -{ - Vector3 result = { -v.x, -v.y, -v.z }; - return result; -} - -// Normalize provided vector -RMDEF Vector3 Vector3Normalize(Vector3 v) -{ - Vector3 result = v; - - float length, ilength; - length = Vector3Length(v); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; - - return result; -} - -// Orthonormalize provided vectors -// Makes vectors normalized and orthogonal to each other -// Gram-Schmidt function implementation -RMDEF void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) -{ - *v1 = Vector3Normalize(*v1); - Vector3 vn = Vector3CrossProduct(*v1, *v2); - vn = Vector3Normalize(vn); - *v2 = Vector3CrossProduct(vn, *v1); -} - -// Transforms a Vector3 by a given Matrix -RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) -{ - Vector3 result = { 0 }; - float x = v.x; - float y = v.y; - float z = v.z; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - - return result; -}; - -// Transform a vector by quaternion rotation -RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) -{ - Vector3 result = { 0 }; - - result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); - result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); - result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); - - return result; -} - -// Calculate linear interpolation between two vectors -RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) -{ - Vector3 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - - return result; -} - -// Calculate reflected vector to normal -RMDEF Vector3 Vector3Reflect(Vector3 v, Vector3 normal) -{ - // I is the original vector - // N is the normal of the incident plane - // R = I - (2*N*( DotProduct[ I,N] )) - - Vector3 result = { 0 }; - - float dotProduct = Vector3DotProduct(v, normal); - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - result.z = v.z - (2.0f*normal.z)*dotProduct; - - return result; -} - -// Return min value for each pair of components -RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - - return result; -} - -// Return max value for each pair of components -RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - - return result; -} - -// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) -// NOTE: Assumes P is on the plane of the triangle -RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) -{ - //Vector v0 = b - a, v1 = c - a, v2 = p - a; - - Vector3 v0 = Vector3Subtract(b, a); - Vector3 v1 = Vector3Subtract(c, a); - Vector3 v2 = Vector3Subtract(p, a); - float d00 = Vector3DotProduct(v0, v0); - float d01 = Vector3DotProduct(v0, v1); - float d11 = Vector3DotProduct(v1, v1); - float d20 = Vector3DotProduct(v2, v0); - float d21 = Vector3DotProduct(v2, v1); - - float denom = d00*d11 - d01*d01; - - Vector3 result = { 0 }; - - result.y = (d11*d20 - d01*d21)/denom; - result.z = (d00*d21 - d01*d20)/denom; - result.x = 1.0f - (result.z + result.y); - - return result; -} - -// Returns Vector3 as float array -RMDEF float3 Vector3ToFloatV(Vector3 v) -{ - float3 buffer = { 0 }; - - buffer.v[0] = v.x; - buffer.v[1] = v.y; - buffer.v[2] = v.z; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix math -//---------------------------------------------------------------------------------- - -// Compute matrix determinant -RMDEF float MatrixDeterminant(Matrix mat) -{ - float result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + - a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + - a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + - a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + - a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + - a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; - - return result; -} - -// Returns the trace of the matrix (sum of the values along the diagonal) -RMDEF float MatrixTrace(Matrix mat) -{ - float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); - return result; -} - -// Transposes provided matrix -RMDEF Matrix MatrixTranspose(Matrix mat) -{ - Matrix result = { 0 }; - - result.m0 = mat.m0; - result.m1 = mat.m4; - result.m2 = mat.m8; - result.m3 = mat.m12; - result.m4 = mat.m1; - result.m5 = mat.m5; - result.m6 = mat.m9; - result.m7 = mat.m13; - result.m8 = mat.m2; - result.m9 = mat.m6; - result.m10 = mat.m10; - result.m11 = mat.m14; - result.m12 = mat.m3; - result.m13 = mat.m7; - result.m14 = mat.m11; - result.m15 = mat.m15; - - return result; -} - -// Invert provided matrix -RMDEF Matrix MatrixInvert(Matrix mat) -{ - Matrix result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; - result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; - result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; - result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; - result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; - result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; - result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; - result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; - result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; - result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; - result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; - result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; - result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; - result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; - result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; - result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - - return result; -} - -// Normalize provided matrix -RMDEF Matrix MatrixNormalize(Matrix mat) -{ - Matrix result = { 0 }; - - float det = MatrixDeterminant(mat); - - result.m0 = mat.m0/det; - result.m1 = mat.m1/det; - result.m2 = mat.m2/det; - result.m3 = mat.m3/det; - result.m4 = mat.m4/det; - result.m5 = mat.m5/det; - result.m6 = mat.m6/det; - result.m7 = mat.m7/det; - result.m8 = mat.m8/det; - result.m9 = mat.m9/det; - result.m10 = mat.m10/det; - result.m11 = mat.m11/det; - result.m12 = mat.m12/det; - result.m13 = mat.m13/det; - result.m14 = mat.m14/det; - result.m15 = mat.m15/det; - - return result; -} - -// Returns identity matrix -RMDEF Matrix MatrixIdentity(void) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Add two matrices -RMDEF Matrix MatrixAdd(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 + right.m0; - result.m1 = left.m1 + right.m1; - result.m2 = left.m2 + right.m2; - result.m3 = left.m3 + right.m3; - result.m4 = left.m4 + right.m4; - result.m5 = left.m5 + right.m5; - result.m6 = left.m6 + right.m6; - result.m7 = left.m7 + right.m7; - result.m8 = left.m8 + right.m8; - result.m9 = left.m9 + right.m9; - result.m10 = left.m10 + right.m10; - result.m11 = left.m11 + right.m11; - result.m12 = left.m12 + right.m12; - result.m13 = left.m13 + right.m13; - result.m14 = left.m14 + right.m14; - result.m15 = left.m15 + right.m15; - - return result; -} - -// Substract two matrices (left - right) -RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 - right.m0; - result.m1 = left.m1 - right.m1; - result.m2 = left.m2 - right.m2; - result.m3 = left.m3 - right.m3; - result.m4 = left.m4 - right.m4; - result.m5 = left.m5 - right.m5; - result.m6 = left.m6 - right.m6; - result.m7 = left.m7 - right.m7; - result.m8 = left.m8 - right.m8; - result.m9 = left.m9 - right.m9; - result.m10 = left.m10 - right.m10; - result.m11 = left.m11 - right.m11; - result.m12 = left.m12 - right.m12; - result.m13 = left.m13 - right.m13; - result.m14 = left.m14 - right.m14; - result.m15 = left.m15 - right.m15; - - return result; -} - -// Returns translation matrix -RMDEF Matrix MatrixTranslate(float x, float y, float z) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Create rotation matrix from axis and angle -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixRotate(Vector3 axis, float angle) -{ - Matrix result = { 0 }; - - float x = axis.x, y = axis.y, z = axis.z; - - float length = sqrtf(x*x + y*y + z*z); - - if ((length != 1.0f) && (length != 0.0f)) - { - length = 1.0f/length; - x *= length; - y *= length; - z *= length; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - result.m0 = x*x*t + cosres; - result.m1 = y*x*t + z*sinres; - result.m2 = z*x*t - y*sinres; - result.m3 = 0.0f; - - result.m4 = x*y*t - z*sinres; - result.m5 = y*y*t + cosres; - result.m6 = z*y*t + x*sinres; - result.m7 = 0.0f; - - result.m8 = x*z*t + y*sinres; - result.m9 = y*z*t - x*sinres; - result.m10 = z*z*t + cosres; - result.m11 = 0.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns x-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateX(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m5 = cosres; - result.m6 = -sinres; - result.m9 = sinres; - result.m10 = cosres; - - return result; -} - -// Returns y-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateY(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m2 = sinres; - result.m8 = -sinres; - result.m10 = cosres; - - return result; -} - -// Returns z-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateZ(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m1 = -sinres; - result.m4 = sinres; - result.m5 = cosres; - - return result; -} - -// Returns scaling matrix -RMDEF Matrix MatrixScale(float x, float y, float z) -{ - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Returns two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -RMDEF Matrix MatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -// Returns perspective projection matrix -RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = (near*2.0f)/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - - result.m4 = 0.0f; - result.m5 = (near*2.0f)/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - - result.m8 = (right + left)/rl; - result.m9 = (top + bottom)/tb; - result.m10 = -(far + near)/fn; - result.m11 = -1.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = -(far*near*2.0f)/fn; - result.m15 = 0.0f; - - return result; -} - -// Returns perspective projection matrix -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far) -{ - double top = near*tan(fovy*0.5); - double right = top*aspect; - Matrix result = MatrixFrustum(-right, right, -top, top, near, far); - - return result; -} - -// Returns orthographic projection matrix -RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = 2.0f/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - result.m4 = 0.0f; - result.m5 = 2.0f/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - result.m8 = 0.0f; - result.m9 = 0.0f; - result.m10 = -2.0f/fn; - result.m11 = 0.0f; - result.m12 = -(left + right)/rl; - result.m13 = -(top + bottom)/tb; - result.m14 = -(far + near)/fn; - result.m15 = 1.0f; - - return result; -} - -// Returns camera look-at matrix (view matrix) -RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) -{ - Matrix result = { 0 }; - - Vector3 z = Vector3Subtract(eye, target); - z = Vector3Normalize(z); - Vector3 x = Vector3CrossProduct(up, z); - x = Vector3Normalize(x); - Vector3 y = Vector3CrossProduct(z, x); - y = Vector3Normalize(y); - - result.m0 = x.x; - result.m1 = x.y; - result.m2 = x.z; - result.m3 = 0.0f; - result.m4 = y.x; - result.m5 = y.y; - result.m6 = y.z; - result.m7 = 0.0f; - result.m8 = z.x; - result.m9 = z.y; - result.m10 = z.z; - result.m11 = 0.0f; - result.m12 = eye.x; - result.m13 = eye.y; - result.m14 = eye.z; - result.m15 = 1.0f; - - result = MatrixInvert(result); - - return result; -} - -// Returns float array of matrix data -RMDEF float16 MatrixToFloatV(Matrix mat) -{ - float16 buffer = { 0 }; - - buffer.v[0] = mat.m0; - buffer.v[1] = mat.m1; - buffer.v[2] = mat.m2; - buffer.v[3] = mat.m3; - buffer.v[4] = mat.m4; - buffer.v[5] = mat.m5; - buffer.v[6] = mat.m6; - buffer.v[7] = mat.m7; - buffer.v[8] = mat.m8; - buffer.v[9] = mat.m9; - buffer.v[10] = mat.m10; - buffer.v[11] = mat.m11; - buffer.v[12] = mat.m12; - buffer.v[13] = mat.m13; - buffer.v[14] = mat.m14; - buffer.v[15] = mat.m15; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Quaternion math -//---------------------------------------------------------------------------------- - -// Returns identity quaternion -RMDEF Quaternion QuaternionIdentity(void) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - return result; -} - -// Computes the length of a quaternion -RMDEF float QuaternionLength(Quaternion q) -{ - float result = sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - return result; -} - -// Normalize provided quaternion -RMDEF Quaternion QuaternionNormalize(Quaternion q) -{ - Quaternion result = { 0 }; - - float length, ilength; - length = QuaternionLength(q); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Invert provided quaternion -RMDEF Quaternion QuaternionInvert(Quaternion q) -{ - Quaternion result = q; - float length = QuaternionLength(q); - float lengthSq = length*length; - - if (lengthSq != 0.0) - { - float i = 1.0f/lengthSq; - - result.x *= -i; - result.y *= -i; - result.z *= -i; - result.w *= i; - } - - return result; -} - -// Calculate two quaternion multiplication -RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) -{ - Quaternion result = { 0 }; - - float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; - float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; - - result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; - result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; - result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; - result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; - - return result; -} - -// Calculate linear interpolation between two quaternions -RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - return result; -} - -// Calculate slerp-optimized interpolation between two quaternions -RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = QuaternionLerp(q1, q2, amount); - result = QuaternionNormalize(result); - - return result; -} - -// Calculates spherical linear interpolation between two quaternions -RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - - if (fabs(cosHalfTheta) >= 1.0f) result = q1; - else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); - else - { - float halfTheta = acos(cosHalfTheta); - float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); - - if (fabs(sinHalfTheta) < 0.001f) - { - result.x = (q1.x*0.5f + q2.x*0.5f); - result.y = (q1.y*0.5f + q2.y*0.5f); - result.z = (q1.z*0.5f + q2.z*0.5f); - result.w = (q1.w*0.5f + q2.w*0.5f); - } - else - { - float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; - float ratioB = sinf(amount*halfTheta)/sinHalfTheta; - - result.x = (q1.x*ratioA + q2.x*ratioB); - result.y = (q1.y*ratioA + q2.y*ratioB); - result.z = (q1.z*ratioA + q2.z*ratioB); - result.w = (q1.w*ratioA + q2.w*ratioB); - } - } - - return result; -} - -// Calculate quaternion based on the rotation from one vector to another -RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) -{ - Quaternion result = { 0 }; - - float cos2Theta = Vector3DotProduct(from, to); - Vector3 cross = Vector3CrossProduct(from, to); - - result.x = cross.x; - result.y = cross.y; - result.z = cross.y; - result.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity() - - // Normalize to essentially nlerp the original and identity to 0.5 - result = QuaternionNormalize(result); - - // Above lines are equivalent to: - //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); - - return result; -} - -// Returns a quaternion for a given rotation matrix -RMDEF Quaternion QuaternionFromMatrix(Matrix mat) -{ - Quaternion result = { 0 }; - - float trace = MatrixTrace(mat); - - if (trace > 0.0f) - { - float s = (float)sqrt(trace + 1)*2.0f; - float invS = 1.0f/s; - - result.w = s*0.25f; - result.x = (mat.m6 - mat.m9)*invS; - result.y = (mat.m8 - mat.m2)*invS; - result.z = (mat.m1 - mat.m4)*invS; - } - else - { - float m00 = mat.m0, m11 = mat.m5, m22 = mat.m10; - - if (m00 > m11 && m00 > m22) - { - float s = (float)sqrt(1.0f + m00 - m11 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m6 - mat.m9)*invS; - result.x = s*0.25f; - result.y = (mat.m4 + mat.m1)*invS; - result.z = (mat.m8 + mat.m2)*invS; - } - else if (m11 > m22) - { - float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m8 - mat.m2)*invS; - result.x = (mat.m4 + mat.m1)*invS; - result.y = s*0.25f; - result.z = (mat.m9 + mat.m6)*invS; - } - else - { - float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m1 - mat.m4)*invS; - result.x = (mat.m8 + mat.m2)*invS; - result.y = (mat.m9 + mat.m6)*invS; - result.z = s*0.25f; - } - } - - return result; -} - -// Returns a matrix for a given quaternion -RMDEF Matrix QuaternionToMatrix(Quaternion q) -{ - Matrix result = { 0 }; - - float x = q.x, y = q.y, z = q.z, w = q.w; - - float x2 = x + x; - float y2 = y + y; - float z2 = z + z; - - float length = QuaternionLength(q); - float lengthSquared = length*length; - - float xx = x*x2/lengthSquared; - float xy = x*y2/lengthSquared; - float xz = x*z2/lengthSquared; - - float yy = y*y2/lengthSquared; - float yz = y*z2/lengthSquared; - float zz = z*z2/lengthSquared; - - float wx = w*x2/lengthSquared; - float wy = w*y2/lengthSquared; - float wz = w*z2/lengthSquared; - - result.m0 = 1.0f - (yy + zz); - result.m1 = xy - wz; - result.m2 = xz + wy; - result.m3 = 0.0f; - result.m4 = xy + wz; - result.m5 = 1.0f - (xx + zz); - result.m6 = yz - wx; - result.m7 = 0.0f; - result.m8 = xz - wy; - result.m9 = yz + wx; - result.m10 = 1.0f - (xx + yy); - result.m11 = 0.0f; - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns rotation quaternion for an angle and axis -// NOTE: angle must be provided in radians -RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - if (Vector3Length(axis) != 0.0f) - - angle *= 0.5f; - - axis = Vector3Normalize(axis); - - float sinres = sinf(angle); - float cosres = cosf(angle); - - result.x = axis.x*sinres; - result.y = axis.y*sinres; - result.z = axis.z*sinres; - result.w = cosres; - - result = QuaternionNormalize(result); - - return result; -} - -// Returns the rotation angle and axis for a given quaternion -RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) -{ - if (fabs(q.w) > 1.0f) q = QuaternionNormalize(q); - - Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; - float resAngle = 0.0f; - - resAngle = 2.0f*(float)acos(q.w); - float den = (float)sqrt(1.0f - q.w*q.w); - - if (den > 0.0001f) - { - resAxis.x = q.x/den; - resAxis.y = q.y/den; - resAxis.z = q.z/den; - } - else - { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. - resAxis.x = 1.0f; - } - - *outAxis = resAxis; - *outAngle = resAngle; -} - -// Returns he quaternion equivalent to Euler angles -RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) -{ - Quaternion q = { 0 }; - - float x0 = cosf(roll*0.5f); - float x1 = sinf(roll*0.5f); - float y0 = cosf(pitch*0.5f); - float y1 = sinf(pitch*0.5f); - float z0 = cosf(yaw*0.5f); - float z1 = sinf(yaw*0.5f); - - q.x = x1*y0*z0 - x0*y1*z1; - q.y = x0*y1*z0 + x1*y0*z1; - q.z = x0*y0*z1 - x1*y1*z0; - q.w = x0*y0*z0 + x1*y1*z1; - - return q; -} - -// Return the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct in degrees -RMDEF Vector3 QuaternionToEuler(Quaternion q) -{ - Vector3 result = { 0 }; - - // roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1)*RAD2DEG; - - // pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0)*RAD2DEG; - - // yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1)*RAD2DEG; - - return result; -} - -// Transform a quaternion given a transformation matrix -RMDEF Quaternion QuaternionTransform(Quaternion q, Matrix mat) -{ - Quaternion result = { 0 }; - - result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; - result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; - result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; - result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - - return result; -} - -#endif // RAYMATH_H diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h deleted file mode 100644 index 41ef8a14..00000000 --- a/examples/others/iqm_loader/riqm.h +++ /dev/null @@ -1,98 +0,0 @@ -/********************************************************************************************** -* -* riqm - InterQuake Model format (IQM) loader for animated meshes -* -* CONFIGURATION: -* -* #define RIQM_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2018 Jonas Daeyaert (@culacant) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RIQM_H -#define RIQM_H - -//#define RIQM_STATIC -#ifdef RIQM_STATIC - #define RIQMDEF static // Functions just visible to module including this file -#else - #ifdef __cplusplus - #define RIQMDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define RIQMDEF extern // Functions visible from other files - #endif -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -#define BONE_NAME_LENGTH 32 // BoneInfo name string length -#define MESH_NAME_LENGTH 32 // Mesh name string length - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - - -#endif // RIQM_H - - -/*********************************************************************************** -* -* RIQM IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RIQM_IMPLEMENTATION) - -//#include "utils.h" // Required for: fopen() Android mapping - -#include // Required for: FILE, fopen(), fclose(), feof(), fseek(), fread() -#include // Required for: malloc(), free() -#include // Required for: strncmp(),strcpy() - -#include "raymath.h" // Required for: Vector3, Quaternion functions - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - - - - -#endif -- cgit v1.2.3 From 99537efccf077c0425c1de1188df632bfe96d125 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:29:53 +0200 Subject: Review some examples --- examples/Makefile | 2 +- examples/core/core_input_multitouch.c | 87 ++++++++++++++++++++++++++++ examples/core/core_input_multitouch.png | Bin 0 -> 17177 bytes examples/core/core_multitouch.c | 90 ----------------------------- examples/core/core_multitouch.png | Bin 17177 -> 0 bytes examples/textures/textures_srcrec_dstrec.c | 8 +-- 6 files changed, 92 insertions(+), 95 deletions(-) create mode 100644 examples/core/core_input_multitouch.c create mode 100644 examples/core/core_input_multitouch.png delete mode 100644 examples/core/core_multitouch.c delete mode 100644 examples/core/core_multitouch.png (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile index b35e39f9..2ceb3f7d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -377,7 +377,7 @@ EXAMPLES = \ core/core_2d_camera \ core/core_world_screen \ core/core_vr_simulator \ - core/core_multitouch \ + core/core_input_multitouch \ shapes/shapes_logo_raylib \ shapes/shapes_basic_shapes \ shapes/shapes_colors_palette \ diff --git a/examples/core/core_input_multitouch.c b/examples/core/core_input_multitouch.c new file mode 100644 index 00000000..ef8fa7ae --- /dev/null +++ b/examples/core/core_input_multitouch.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [core] example - Input multitouch +* +* This example has been created using raylib 2.1 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014-2019 Berni and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch"); + + Vector2 ballPosition = { -100.0f, -100.0f }; + Color ballColor = BEIGE; + + int touchCounter = 0; + Vector2 touchPosition; + + SetTargetFPS(60); + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + ballPosition = GetMousePosition(); + + ballColor = BEIGE; + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) ballColor = MAROON; + if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) ballColor = LIME; + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) ballColor = DARKBLUE; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) touchCounter = 10; + + if (touchCounter > 0) touchCounter--; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Multitouch + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) + { + touchPosition = GetTouchPosition(i); // Get the touch point + + if ((touchPosition.x >= 0) && (touchPosition.y >= 0)) // Make sure point is not (-1,-1) as this means there is no touch for it + { + // Draw circle and touch index number + DrawCircleV(touchPosition, 34, ORANGE); + DrawText(FormatText("%d", i), touchPosition.x - 10, touchPosition.y - 70, 40, BLACK); + } + } + + // Draw the normal mouse location + DrawCircleV(ballPosition, 30 + (touchCounter*3), ballColor); + + DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); + DrawText("touch the screen at multiple locations to get multiple balls", 10, 30, 20, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_input_multitouch.png b/examples/core/core_input_multitouch.png new file mode 100644 index 00000000..74284f82 Binary files /dev/null and b/examples/core/core_input_multitouch.png differ diff --git a/examples/core/core_multitouch.c b/examples/core/core_multitouch.c deleted file mode 100644 index c059ac03..00000000 --- a/examples/core/core_multitouch.c +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************************* -* -* raylib [core] example - Multitouch input -* -* This example has been created using raylib 2.1 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2014 Ramon Santamaria (@raysan5) -* Example by Berni -* -********************************************************************************************/ - -#include "raylib.h" -#include - -int main() -{ - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - multitouch input"); - - Vector2 ballPosition = { -100.0f, -100.0f }; - Color ballColor; - int PressedCounter = 0; - Vector2 TouchPos; - char Str[16]; - - SetTargetFPS(60); - //--------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - ballPosition = GetMousePosition(); - - ballColor = BEIGE; - - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) ballColor = MAROON; - if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) ballColor = LIME; - if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) ballColor = DARKBLUE; - - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) PressedCounter = 10; - if(PressedCounter > 0) - PressedCounter--; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - // Multitouch - for (int i = 0; i < MAX_TOUCH_POINTS; ++i) - { - TouchPos = GetTouchPosition(i); // Get the touch point - - if( (TouchPos.x >= 0) && (TouchPos.y >= 0) ) // Make sure point is not (-1,-1) as this means there is no touch for it - { - DrawCircleV(TouchPos, 34, ORANGE); // Draw a circle there - - sprintf(Str,"%d",i); - DrawText(Str, TouchPos.x - 10, TouchPos.y - 70, 40, BLACK); // Also show its index number - } - } - - // Draw the normal mouse location - DrawCircleV(ballPosition, 30 + (PressedCounter * 3), ballColor); - - DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); - DrawText("touch the screen at multiple locations to get multiple balls", 10, 30, 20, DARKGRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} \ No newline at end of file diff --git a/examples/core/core_multitouch.png b/examples/core/core_multitouch.png deleted file mode 100644 index 74284f82..00000000 Binary files a/examples/core/core_multitouch.png and /dev/null differ diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c index cc08eb58..298a0c65 100644 --- a/examples/textures/textures_srcrec_dstrec.c +++ b/examples/textures/textures_srcrec_dstrec.c @@ -27,13 +27,13 @@ int main() int frameHeight = scarfy.height; // NOTE: Source rectangle (part of the texture to use for drawing) - Rectangle sourceRec = { 0.0f, 0.0f, (float)frameWidth, (float)frameHeight }; + Rectangle sourceRec = { 0.0f, 0.0f, frameWidth, frameHeight }; // NOTE: Destination rectangle (screen rectangle where drawing part of texture) - Rectangle destRec = { (float)screenWidth/2, (float)screenHeight/2, (float)frameWidth*2, (float)frameHeight*2 }; + Rectangle destRec = { screenWidth/2, screenHeight/2, frameWidth*2, frameHeight*2 }; // NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size - Vector2 origin = { (float)frameWidth, (float)frameHeight }; + Vector2 origin = { frameWidth, frameHeight }; int rotation = 0; @@ -61,7 +61,7 @@ int main() // rotation defines the texture rotation (using origin as rotation point) DrawTexturePro(scarfy, sourceRec, destRec, origin, (float)rotation, WHITE); - DrawLine((int) destRec.x, 0, (int) destRec.x, screenHeight, GRAY); + DrawLine((int)destRec.x, 0, (int)destRec.x, screenHeight, GRAY); DrawLine(0, (int)destRec.y, screenWidth, (int)destRec.y, GRAY); DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY); -- cgit v1.2.3 From 86c887f0cf2457503b13e900b6018fd596468389 Mon Sep 17 00:00:00 2001 From: Demizdor Date: Sun, 21 Apr 2019 13:44:17 +0300 Subject: Added unicode example --- examples/text/resources/dejavu.fnt | 1115 +++++++++++++++++++++++++++++++++ examples/text/resources/dejavu.png | Bin 0 -> 102287 bytes examples/text/resources/emoji.fnt | 191 ++++++ examples/text/resources/emoji_0.png | Bin 0 -> 244961 bytes examples/text/resources/notoCJK.fnt | 580 +++++++++++++++++ examples/text/resources/notoCJK_0.png | Bin 0 -> 92202 bytes examples/text/text_unicode.c | 311 +++++++++ 7 files changed, 2197 insertions(+) create mode 100644 examples/text/resources/dejavu.fnt create mode 100644 examples/text/resources/dejavu.png create mode 100644 examples/text/resources/emoji.fnt create mode 100644 examples/text/resources/emoji_0.png create mode 100644 examples/text/resources/notoCJK.fnt create mode 100644 examples/text/resources/notoCJK_0.png create mode 100644 examples/text/text_unicode.c (limited to 'examples') diff --git a/examples/text/resources/dejavu.fnt b/examples/text/resources/dejavu.fnt new file mode 100644 index 00000000..6503b353 --- /dev/null +++ b/examples/text/resources/dejavu.fnt @@ -0,0 +1,1115 @@ +info face="DejaVu Sans" size=-16 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 outline=1 +common lineHeight=19 base=15 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 +page id=0 file="dejavu.png" +chars count=1111 +char id=32 x=259 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=33 x=464 y=382 width=4 height=14 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=34 x=112 y=461 width=7 height=7 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 +char id=35 x=33 y=407 width=14 height=13 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=15 +char id=36 x=381 y=153 width=10 height=16 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=37 x=493 y=222 width=17 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=38 x=275 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=39 x=506 y=395 width=4 height=7 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=40 x=18 y=178 width=6 height=16 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=41 x=10 y=178 width=6 height=16 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=42 x=192 y=444 width=10 height=11 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=43 x=369 y=415 width=13 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=44 x=474 y=450 width=5 height=6 xoffset=0 yoffset=12 xadvance=5 page=0 chnl=15 +char id=45 x=191 y=468 width=7 height=4 xoffset=-1 yoffset=9 xadvance=6 page=0 chnl=15 +char id=46 x=207 y=465 width=5 height=4 xoffset=0 yoffset=12 xadvance=5 page=0 chnl=15 +char id=47 x=331 y=224 width=8 height=15 xoffset=-1 yoffset=2 xadvance=5 page=0 chnl=15 +char id=48 x=396 y=304 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=49 x=290 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=50 x=386 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=51 x=374 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=52 x=466 y=302 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=53 x=338 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=54 x=273 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=55 x=326 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=56 x=286 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=57 x=42 y=327 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=58 x=428 y=441 width=5 height=10 xoffset=0 yoffset=6 xadvance=5 page=0 chnl=15 +char id=59 x=472 y=398 width=5 height=12 xoffset=0 yoffset=6 xadvance=5 page=0 chnl=15 +char id=60 x=339 y=415 width=13 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=61 x=42 y=461 width=13 height=7 xoffset=0 yoffset=7 xadvance=13 page=0 chnl=15 +char id=62 x=324 y=415 width=13 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=63 x=501 y=238 width=9 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=64 x=368 y=135 width=17 height=16 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=65 x=60 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=66 x=299 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=67 x=90 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=68 x=105 y=294 width=13 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=69 x=325 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=70 x=194 y=371 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=71 x=208 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=72 x=420 y=320 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=73 x=423 y=384 width=5 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=74 x=237 y=136 width=7 height=17 xoffset=-2 yoffset=2 xadvance=5 page=0 chnl=15 +char id=75 x=60 y=311 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=76 x=72 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=77 x=288 y=273 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=78 x=74 y=311 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=79 x=320 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=80 x=351 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=81 x=467 y=135 width=14 height=16 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=82 x=102 y=311 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=83 x=116 y=310 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=84 x=130 y=310 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=85 x=144 y=307 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=86 x=496 y=270 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=87 x=164 y=242 width=18 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=88 x=150 y=291 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=89 x=158 y=307 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=90 x=210 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=91 x=34 y=178 width=6 height=16 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=92 x=321 y=224 width=8 height=15 xoffset=-1 yoffset=2 xadvance=5 page=0 chnl=15 +char id=93 x=26 y=178 width=6 height=16 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=94 x=27 y=461 width=13 height=7 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=95 x=107 y=470 width=12 height=4 xoffset=-2 yoffset=16 xadvance=8 page=0 chnl=15 +char id=96 x=398 y=453 width=7 height=6 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=15 +char id=97 x=312 y=428 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=98 x=455 y=350 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=99 x=36 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=100 x=468 y=350 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=101 x=286 y=428 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=102 x=311 y=385 width=8 height=14 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=103 x=65 y=375 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=104 x=410 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=105 x=476 y=382 width=4 height=14 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=106 x=289 y=136 width=6 height=17 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=107 x=130 y=374 width=11 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=108 x=482 y=382 width=4 height=14 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=109 x=93 y=422 width=16 height=11 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=110 x=495 y=424 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=111 x=260 y=430 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=112 x=143 y=374 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=113 x=156 y=371 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=114 x=292 y=441 width=8 height=11 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15 +char id=115 x=435 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=116 x=229 y=402 width=8 height=13 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=117 x=411 y=428 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=118 x=143 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=119 x=213 y=418 width=15 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=120 x=130 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=121 x=476 y=318 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=122 x=363 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=123 x=60 y=141 width=10 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=124 x=469 y=20 width=5 height=18 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=125 x=48 y=141 width=10 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=126 x=266 y=456 width=13 height=6 xoffset=0 yoffset=7 xadvance=13 page=0 chnl=15 +char id=160 x=284 y=463 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=161 x=506 y=153 width=4 height=14 xoffset=1 yoffset=5 xadvance=6 page=0 chnl=15 +char id=162 x=492 y=205 width=10 height=15 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=163 x=0 y=343 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=164 x=440 y=414 width=12 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=165 x=0 y=327 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=166 x=42 y=178 width=5 height=16 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 +char id=167 x=345 y=154 width=10 height=16 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=168 x=502 y=447 width=8 height=5 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=169 x=352 y=273 width=14 height=14 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=170 x=237 y=443 width=9 height=11 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=171 x=337 y=441 width=10 height=10 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=15 +char id=172 x=462 y=440 width=13 height=8 xoffset=0 yoffset=7 xadvance=13 page=0 chnl=15 +char id=173 x=155 y=470 width=7 height=4 xoffset=-1 yoffset=9 xadvance=6 page=0 chnl=15 +char id=174 x=400 y=272 width=14 height=14 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=175 x=145 y=470 width=8 height=4 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=176 x=0 y=461 width=8 height=8 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=177 x=337 y=401 width=13 height=12 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=178 x=392 y=441 width=8 height=10 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=179 x=382 y=441 width=8 height=10 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=180 x=380 y=453 width=7 height=6 xoffset=1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=181 x=169 y=371 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=182 x=393 y=153 width=10 height=16 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=183 x=14 y=471 width=5 height=5 xoffset=0 yoffset=7 xadvance=5 page=0 chnl=15 +char id=184 x=423 y=453 width=6 height=6 xoffset=1 yoffset=13 xadvance=8 page=0 chnl=15 +char id=185 x=411 y=441 width=7 height=10 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=186 x=259 y=443 width=9 height=11 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=187 x=349 y=441 width=10 height=10 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=15 +char id=188 x=411 y=238 width=16 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=189 x=447 y=238 width=16 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=190 x=356 y=240 width=17 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=191 x=228 y=386 width=9 height=14 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=192 x=135 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=193 x=150 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=194 x=165 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=195 x=180 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=196 x=195 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=197 x=210 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=198 x=299 y=241 width=17 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=199 x=225 y=79 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=200 x=119 y=118 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=201 x=106 y=119 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=202 x=80 y=119 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=203 x=353 y=116 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=204 x=504 y=97 width=6 height=17 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=205 x=321 y=136 width=6 height=17 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=206 x=187 y=136 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=207 x=147 y=136 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=208 x=448 y=270 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=209 x=420 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=210 x=112 y=61 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=211 x=32 y=65 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=212 x=128 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=213 x=457 y=40 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=214 x=473 y=40 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=215 x=380 y=401 width=12 height=12 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=216 x=451 y=135 width=14 height=16 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=217 x=329 y=79 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=218 x=476 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=219 x=308 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=220 x=70 y=100 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=221 x=56 y=103 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=222 x=26 y=375 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=223 x=0 y=375 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=224 x=52 y=213 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=225 x=39 y=213 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=226 x=26 y=213 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=227 x=494 y=350 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=228 x=481 y=350 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=229 x=293 y=155 width=11 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=230 x=0 y=422 width=17 height=11 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=231 x=192 y=387 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=232 x=478 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=233 x=452 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=234 x=426 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=235 x=429 y=352 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=236 x=380 y=223 width=7 height=15 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=237 x=371 y=223 width=7 height=15 xoffset=-1 yoffset=1 xadvance=4 page=0 chnl=15 +char id=238 x=351 y=223 width=8 height=15 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=239 x=321 y=385 width=8 height=14 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=240 x=403 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=241 x=422 y=368 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=242 x=286 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=243 x=260 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=244 x=234 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=245 x=364 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=246 x=338 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=247 x=310 y=441 width=13 height=10 xoffset=0 yoffset=6 xadvance=13 page=0 chnl=15 +char id=248 x=64 y=407 width=12 height=13 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=249 x=96 y=229 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=250 x=84 y=229 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=251 x=24 y=230 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=252 x=60 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=253 x=315 y=20 width=11 height=18 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=254 x=158 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=255 x=171 y=117 width=11 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=256 x=45 y=160 width=13 height=16 xoffset=-1 yoffset=0 xadvance=11 page=0 chnl=15 +char id=257 x=312 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=258 x=0 y=26 width=13 height=18 xoffset=-1 yoffset=-2 xadvance=11 page=0 chnl=15 +char id=259 x=195 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=260 x=80 y=62 width=14 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=261 x=322 y=321 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=262 x=255 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=263 x=312 y=207 width=10 height=15 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=264 x=270 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=265 x=384 y=206 width=10 height=15 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=266 x=285 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=267 x=24 y=391 width=10 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=268 x=300 y=79 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=269 x=456 y=205 width=10 height=15 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=270 x=288 y=60 width=13 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=271 x=432 y=270 width=14 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=272 x=368 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=273 x=266 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=274 x=254 y=155 width=11 height=16 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=275 x=234 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=276 x=197 y=117 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=277 x=169 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=278 x=210 y=117 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=279 x=247 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=280 x=223 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=281 x=260 y=353 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=282 x=236 y=117 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=283 x=156 y=208 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=284 x=425 y=40 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=285 x=172 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=286 x=377 y=40 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=287 x=328 y=20 width=11 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=288 x=48 y=62 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=289 x=444 y=116 width=11 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=290 x=394 y=0 width=14 height=18 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=291 x=185 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=292 x=42 y=103 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=293 x=322 y=98 width=12 height=17 xoffset=-2 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=294 x=108 y=262 width=15 height=14 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=295 x=298 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=296 x=157 y=136 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=297 x=281 y=385 width=8 height=14 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=298 x=486 y=153 width=8 height=16 xoffset=-2 yoffset=0 xadvance=5 page=0 chnl=15 +char id=299 x=271 y=385 width=8 height=14 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=300 x=107 y=138 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=301 x=311 y=224 width=8 height=15 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=302 x=305 y=136 width=6 height=17 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=303 x=313 y=136 width=6 height=17 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=304 x=505 y=116 width=5 height=17 xoffset=0 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=305 x=506 y=382 width=4 height=11 xoffset=0 yoffset=5 xadvance=4 page=0 chnl=15 +char id=306 x=84 y=138 width=10 height=17 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=307 x=137 y=137 width=8 height=17 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=308 x=101 y=0 width=8 height=20 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=309 x=443 y=20 width=8 height=18 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=310 x=88 y=22 width=12 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=311 x=302 y=21 width=11 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=312 x=0 y=435 width=11 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=313 x=493 y=116 width=10 height=17 xoffset=0 yoffset=-1 xadvance=9 page=0 chnl=15 +char id=314 x=297 y=136 width=6 height=17 xoffset=0 yoffset=-1 xadvance=4 page=0 chnl=15 +char id=315 x=401 y=20 width=10 height=18 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=316 x=461 y=20 width=6 height=18 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15 +char id=317 x=48 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=318 x=406 y=385 width=7 height=14 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=319 x=302 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=320 x=379 y=385 width=7 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=321 x=340 y=305 width=12 height=14 xoffset=-2 yoffset=2 xadvance=9 page=0 chnl=15 +char id=322 x=397 y=385 width=7 height=14 xoffset=-1 yoffset=2 xadvance=5 page=0 chnl=15 +char id=323 x=238 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=324 x=108 y=229 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=325 x=60 y=23 width=12 height=18 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=326 x=144 y=226 width=10 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=327 x=210 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=328 x=336 y=207 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=329 x=315 y=289 width=13 height=14 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=330 x=168 y=98 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=331 x=216 y=387 width=10 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=332 x=483 y=135 width=14 height=16 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=333 x=13 y=343 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=334 x=64 y=62 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=335 x=374 y=189 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=336 x=176 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=337 x=400 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=338 x=63 y=247 width=19 height=14 xoffset=-1 yoffset=2 xadvance=17 page=0 chnl=15 +char id=339 x=486 y=398 width=18 height=11 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=340 x=14 y=103 width=12 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=341 x=236 y=224 width=9 height=15 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15 +char id=342 x=116 y=21 width=12 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=343 x=192 y=224 width=9 height=15 xoffset=-1 yoffset=5 xadvance=7 page=0 chnl=15 +char id=344 x=455 y=78 width=12 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=345 x=291 y=224 width=8 height=15 xoffset=0 yoffset=1 xadvance=7 page=0 chnl=15 +char id=346 x=182 y=98 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=347 x=360 y=206 width=10 height=15 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=348 x=462 y=97 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=349 x=396 y=205 width=10 height=15 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=350 x=497 y=78 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=351 x=180 y=387 width=10 height=14 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=352 x=84 y=100 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=353 x=444 y=205 width=10 height=15 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=354 x=98 y=100 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=355 x=0 y=178 width=8 height=16 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=356 x=126 y=99 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=357 x=361 y=223 width=8 height=15 xoffset=-1 yoffset=1 xadvance=6 page=0 chnl=15 +char id=358 x=294 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=359 x=239 y=402 width=8 height=13 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=360 x=196 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=361 x=84 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=362 x=173 y=155 width=12 height=16 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 +char id=363 x=230 y=370 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=364 x=280 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=365 x=132 y=226 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=366 x=294 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=367 x=453 y=153 width=10 height=16 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=368 x=350 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=369 x=12 y=230 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=370 x=364 y=97 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=371 x=104 y=343 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=372 x=126 y=41 width=18 height=17 xoffset=-1 yoffset=-1 xadvance=16 page=0 chnl=15 +char id=373 x=343 y=172 width=15 height=15 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=374 x=434 y=97 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=375 x=289 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=376 x=14 y=122 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=377 x=105 y=80 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=378 x=348 y=206 width=10 height=15 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=379 x=90 y=81 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=380 x=156 y=387 width=10 height=14 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=381 x=60 y=81 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=382 x=156 y=225 width=10 height=15 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=383 x=331 y=385 width=8 height=14 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=384 x=434 y=320 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=385 x=243 y=257 width=14 height=14 xoffset=-2 yoffset=2 xadvance=12 page=0 chnl=15 +char id=386 x=78 y=375 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=387 x=416 y=352 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=388 x=364 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=389 x=480 y=302 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=390 x=225 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=391 x=310 y=41 width=15 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=392 x=270 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=393 x=259 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=394 x=483 y=238 width=16 height=14 xoffset=-2 yoffset=2 xadvance=13 page=0 chnl=15 +char id=395 x=130 y=358 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=396 x=377 y=337 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=397 x=52 y=359 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=398 x=325 y=337 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=399 x=419 y=254 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=400 x=12 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=401 x=336 y=98 width=12 height=17 xoffset=-2 yoffset=2 xadvance=9 page=0 chnl=15 +char id=402 x=96 y=138 width=9 height=17 xoffset=-2 yoffset=2 xadvance=6 page=0 chnl=15 +char id=403 x=239 y=41 width=16 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=404 x=363 y=59 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=405 x=36 y=263 width=16 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=406 x=361 y=385 width=7 height=14 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=407 x=388 y=385 width=7 height=14 xoffset=-1 yoffset=2 xadvance=5 page=0 chnl=15 +char id=408 x=165 y=291 width=13 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=409 x=442 y=336 width=11 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=410 x=370 y=385 width=7 height=14 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15 +char id=411 x=468 y=334 width=11 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=412 x=429 y=238 width=16 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=413 x=409 y=40 width=14 height=17 xoffset=-2 yoffset=2 xadvance=12 page=0 chnl=15 +char id=414 x=218 y=370 width=10 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=415 x=96 y=278 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=416 x=112 y=278 width=14 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=417 x=352 y=401 width=12 height=12 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=418 x=18 y=263 width=16 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=419 x=450 y=286 width=13 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=420 x=405 y=288 width=13 height=14 xoffset=-2 yoffset=2 xadvance=10 page=0 chnl=15 +char id=421 x=288 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=422 x=159 y=155 width=12 height=16 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=423 x=14 y=327 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=424 x=132 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=425 x=143 y=358 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=426 x=327 y=117 width=11 height=17 xoffset=-4 yoffset=2 xadvance=5 page=0 chnl=15 +char id=427 x=476 y=153 width=8 height=16 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=428 x=326 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=429 x=341 y=385 width=8 height=14 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=430 x=112 y=99 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=431 x=192 y=274 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=432 x=366 y=401 width=12 height=12 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=433 x=240 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=434 x=70 y=327 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=435 x=128 y=275 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=436 x=176 y=274 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=437 x=330 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=438 x=423 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=439 x=489 y=318 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=440 x=242 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=441 x=104 y=375 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=442 x=0 y=230 width=10 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=443 x=350 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=444 x=126 y=326 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=445 x=208 y=354 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=446 x=372 y=206 width=10 height=15 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=447 x=195 y=355 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=448 x=505 y=39 width=5 height=17 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=449 x=117 y=138 width=8 height=17 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=450 x=36 y=141 width=10 height=17 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=15 +char id=451 x=458 y=382 width=4 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=452 x=476 y=20 width=23 height=17 xoffset=0 yoffset=-1 xadvance=23 page=0 chnl=15 +char id=453 x=80 y=178 width=21 height=15 xoffset=0 yoffset=1 xadvance=21 page=0 chnl=15 +char id=454 x=103 y=175 width=19 height=15 xoffset=-1 yoffset=1 xadvance=18 page=0 chnl=15 +char id=455 x=441 y=40 width=14 height=17 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=456 x=393 y=59 width=13 height=17 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=457 x=264 y=136 width=7 height=17 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 +char id=458 x=257 y=41 width=16 height=17 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=459 x=160 y=60 width=14 height=17 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=460 x=0 y=84 width=13 height=17 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=461 x=408 y=59 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=462 x=221 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=463 x=217 y=136 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=464 x=301 y=224 width=8 height=15 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=465 x=272 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=466 x=247 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=467 x=483 y=78 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=468 x=432 y=205 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=469 x=158 y=21 width=12 height=18 xoffset=0 yoffset=-2 xadvance=12 page=0 chnl=15 +char id=470 x=369 y=153 width=10 height=16 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=471 x=242 y=0 width=12 height=19 xoffset=0 yoffset=-3 xadvance=12 page=0 chnl=15 +char id=472 x=72 y=141 width=10 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=473 x=256 y=0 width=12 height=19 xoffset=0 yoffset=-3 xadvance=12 page=0 chnl=15 +char id=474 x=457 y=116 width=10 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=475 x=270 y=0 width=12 height=19 xoffset=0 yoffset=-3 xadvance=12 page=0 chnl=15 +char id=476 x=0 y=141 width=10 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=477 x=91 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=478 x=488 y=0 width=13 height=18 xoffset=-1 yoffset=-2 xadvance=11 page=0 chnl=15 +char id=479 x=332 y=154 width=11 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=480 x=458 y=0 width=13 height=18 xoffset=-1 yoffset=-2 xadvance=11 page=0 chnl=15 +char id=481 x=306 y=155 width=11 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=482 x=349 y=136 width=17 height=16 xoffset=-1 yoffset=0 xadvance=16 page=0 chnl=15 +char id=483 x=318 y=241 width=17 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=484 x=144 y=275 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=485 x=238 y=321 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=486 x=16 y=65 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=487 x=224 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=488 x=490 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=489 x=423 y=59 width=13 height=17 xoffset=-2 yoffset=-1 xadvance=9 page=0 chnl=15 +char id=490 x=96 y=61 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=491 x=78 y=359 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=492 x=182 y=0 width=14 height=19 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=493 x=262 y=117 width=11 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=494 x=54 y=122 width=11 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=495 x=211 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=496 x=433 y=20 width=8 height=18 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=497 x=398 y=222 width=23 height=14 xoffset=0 yoffset=2 xadvance=23 page=0 chnl=15 +char id=498 x=448 y=222 width=21 height=14 xoffset=0 yoffset=2 xadvance=21 page=0 chnl=15 +char id=499 x=0 y=247 width=19 height=14 xoffset=-1 yoffset=2 xadvance=18 page=0 chnl=15 +char id=500 x=208 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=501 x=263 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=502 x=124 y=243 width=18 height=14 xoffset=0 yoffset=2 xadvance=18 page=0 chnl=15 +char id=503 x=67 y=122 width=11 height=17 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=504 x=224 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=505 x=48 y=230 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=506 x=438 y=59 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=507 x=154 y=98 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=508 x=146 y=41 width=17 height=17 xoffset=-1 yoffset=-1 xadvance=16 page=0 chnl=15 +char id=509 x=181 y=173 width=17 height=15 xoffset=-1 yoffset=1 xadvance=16 page=0 chnl=15 +char id=510 x=410 y=0 width=14 height=18 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=511 x=145 y=156 width=12 height=16 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=512 x=453 y=59 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=513 x=273 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=514 x=60 y=160 width=13 height=16 xoffset=-1 yoffset=0 xadvance=11 page=0 chnl=15 +char id=515 x=338 y=337 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=516 x=132 y=118 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=517 x=143 y=209 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=518 x=267 y=155 width=11 height=16 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=519 x=273 y=337 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=520 x=501 y=20 width=9 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=521 x=203 y=224 width=9 height=15 xoffset=-2 yoffset=1 xadvance=4 page=0 chnl=15 +char id=522 x=496 y=153 width=8 height=16 xoffset=-2 yoffset=0 xadvance=5 page=0 chnl=15 +char id=523 x=502 y=318 width=8 height=14 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=524 x=144 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=525 x=104 y=212 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=526 x=387 y=135 width=14 height=16 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=527 x=208 y=338 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=528 x=385 y=78 width=12 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=529 x=214 y=224 width=9 height=15 xoffset=-1 yoffset=1 xadvance=7 page=0 chnl=15 +char id=530 x=201 y=155 width=12 height=16 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=15 +char id=531 x=261 y=385 width=8 height=14 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 +char id=532 x=427 y=78 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=533 x=36 y=230 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=534 x=75 y=160 width=12 height=16 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 +char id=535 x=446 y=368 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=536 x=144 y=21 width=12 height=18 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=537 x=60 y=230 width=10 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=538 x=102 y=22 width=12 height=18 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=539 x=177 y=136 width=8 height=17 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=540 x=341 y=20 width=10 height=18 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=541 x=120 y=226 width=10 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=542 x=378 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=543 x=392 y=97 width=12 height=17 xoffset=-2 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=544 x=406 y=97 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=545 x=327 y=40 width=15 height=17 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=546 x=375 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=547 x=422 y=401 width=11 height=12 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=548 x=240 y=79 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=549 x=458 y=366 width=10 height=14 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=550 x=483 y=59 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=551 x=221 y=354 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=552 x=41 y=122 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=553 x=182 y=355 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=554 x=362 y=0 width=14 height=18 xoffset=-1 yoffset=-2 xadvance=13 page=0 chnl=15 +char id=555 x=215 y=155 width=11 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=556 x=378 y=0 width=14 height=18 xoffset=-1 yoffset=-2 xadvance=13 page=0 chnl=15 +char id=557 x=78 y=212 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=558 x=489 y=39 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=559 x=169 y=355 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=560 x=426 y=0 width=14 height=18 xoffset=-1 yoffset=-2 xadvance=13 page=0 chnl=15 +char id=561 x=319 y=155 width=11 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=562 x=89 y=157 width=12 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=563 x=431 y=116 width=11 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=564 x=227 y=136 width=8 height=17 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=15 +char id=565 x=17 y=407 width=14 height=13 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=566 x=465 y=153 width=9 height=16 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=567 x=415 y=385 width=6 height=14 xoffset=-2 yoffset=5 xadvance=4 page=0 chnl=15 +char id=568 x=104 y=246 width=18 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=569 x=144 y=243 width=18 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=570 x=15 y=160 width=13 height=16 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=571 x=419 y=135 width=14 height=16 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=572 x=146 y=406 width=11 height=13 xoffset=-1 yoffset=4 xadvance=9 page=0 chnl=15 +char id=573 x=39 y=375 width=11 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=574 x=435 y=135 width=14 height=16 xoffset=-2 yoffset=1 xadvance=10 page=0 chnl=15 +char id=575 x=13 y=213 width=11 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=576 x=0 y=213 width=11 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=577 x=200 y=306 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=578 x=399 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=579 x=336 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=580 x=355 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=581 x=390 y=288 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=582 x=250 y=21 width=11 height=18 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=583 x=387 y=188 width=11 height=15 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=584 x=207 y=136 width=8 height=17 xoffset=-2 yoffset=2 xadvance=5 page=0 chnl=15 +char id=585 x=197 y=136 width=8 height=17 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=586 x=203 y=41 width=16 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=587 x=270 y=289 width=13 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=588 x=255 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=589 x=215 y=444 width=9 height=11 xoffset=-1 yoffset=5 xadvance=7 page=0 chnl=15 +char id=590 x=480 y=286 width=13 height=14 xoffset=-2 yoffset=2 xadvance=10 page=0 chnl=15 +char id=591 x=234 y=190 width=12 height=15 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=880 x=108 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=881 x=226 y=444 width=9 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=882 x=291 y=257 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=883 x=266 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=884 x=121 y=461 width=5 height=7 xoffset=0 yoffset=1 xadvance=4 page=0 chnl=15 +char id=885 x=439 y=452 width=5 height=6 xoffset=0 yoffset=13 xadvance=4 page=0 chnl=15 +char id=886 x=350 y=321 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=887 x=471 y=425 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=890 x=446 y=452 width=5 height=6 xoffset=2 yoffset=14 xadvance=8 page=0 chnl=15 +char id=891 x=351 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=892 x=12 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=893 x=48 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=894 x=479 y=398 width=5 height=12 xoffset=0 yoffset=6 xadvance=5 page=0 chnl=15 +char id=895 x=255 y=136 width=7 height=17 xoffset=-2 yoffset=2 xadvance=5 page=0 chnl=15 +char id=900 x=389 y=453 width=7 height=6 xoffset=1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=901 x=435 y=441 width=8 height=9 xoffset=0 yoffset=-2 xadvance=8 page=0 chnl=15 +char id=902 x=90 y=195 width=13 height=15 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=903 x=0 y=471 width=5 height=5 xoffset=0 yoffset=7 xadvance=5 page=0 chnl=15 +char id=904 x=428 y=171 width=15 height=15 xoffset=-2 yoffset=1 xadvance=12 page=0 chnl=15 +char id=905 x=308 y=173 width=16 height=15 xoffset=-2 yoffset=1 xadvance=14 page=0 chnl=15 +char id=906 x=225 y=224 width=9 height=15 xoffset=-2 yoffset=1 xadvance=7 page=0 chnl=15 +char id=908 x=411 y=171 width=15 height=15 xoffset=-2 yoffset=1 xadvance=13 page=0 chnl=15 +char id=910 x=236 y=173 width=16 height=15 xoffset=-2 yoffset=1 xadvance=13 page=0 chnl=15 +char id=911 x=326 y=173 width=15 height=15 xoffset=-2 yoffset=1 xadvance=13 page=0 chnl=15 +char id=912 x=423 y=20 width=8 height=18 xoffset=-2 yoffset=-2 xadvance=5 page=0 chnl=15 +char id=913 x=30 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=914 x=91 y=343 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=915 x=434 y=368 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=916 x=30 y=311 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=917 x=117 y=359 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=918 x=15 y=311 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=919 x=378 y=321 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=920 x=480 y=270 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=921 x=437 y=384 width=5 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=922 x=112 y=327 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=923 x=465 y=286 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=924 x=307 y=257 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=925 x=140 y=326 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=926 x=482 y=366 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=927 x=403 y=256 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=928 x=168 y=323 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=929 x=65 y=343 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=931 x=78 y=343 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=932 x=210 y=322 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=933 x=224 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=934 x=16 y=279 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=935 x=495 y=286 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=936 x=48 y=279 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=937 x=64 y=279 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=938 x=167 y=136 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=939 x=399 y=78 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=940 x=192 y=190 width=12 height=15 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=941 x=180 y=224 width=10 height=15 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=942 x=377 y=20 width=10 height=18 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=943 x=389 y=223 width=7 height=15 xoffset=0 yoffset=1 xadvance=5 page=0 chnl=15 +char id=944 x=365 y=20 width=10 height=18 xoffset=0 yoffset=-2 xadvance=9 page=0 chnl=15 +char id=945 x=426 y=415 width=12 height=11 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=946 x=237 y=21 width=11 height=18 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=947 x=130 y=342 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=948 x=143 y=342 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=949 x=60 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=950 x=24 y=141 width=10 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=951 x=362 y=369 width=10 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=952 x=364 y=337 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=953 x=302 y=441 width=6 height=11 xoffset=0 yoffset=5 xadvance=5 page=0 chnl=15 +char id=954 x=24 y=448 width=10 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=955 x=169 y=339 width=11 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=956 x=182 y=339 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=957 x=221 y=431 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=958 x=12 y=141 width=10 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=959 x=195 y=431 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=960 x=384 y=415 width=12 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=961 x=91 y=375 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=962 x=314 y=369 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=963 x=398 y=415 width=12 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=964 x=169 y=431 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=965 x=0 y=448 width=10 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=966 x=406 y=320 width=12 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=967 x=52 y=375 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=968 x=392 y=321 width=12 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=969 x=128 y=422 width=15 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=970 x=301 y=385 width=8 height=14 xoffset=-2 yoffset=2 xadvance=5 page=0 chnl=15 +char id=971 x=168 y=387 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=972 x=299 y=207 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=973 x=480 y=205 width=10 height=15 xoffset=0 yoffset=1 xadvance=9 page=0 chnl=15 +char id=974 x=394 y=171 width=15 height=15 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=975 x=130 y=21 width=12 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=976 x=278 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=977 x=390 y=353 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=978 x=195 y=290 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=979 x=143 y=174 width=17 height=15 xoffset=-2 yoffset=1 xadvance=13 page=0 chnl=15 +char id=980 x=75 y=81 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=981 x=357 y=78 width=12 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=982 x=145 y=421 width=15 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=983 x=346 y=189 width=12 height=15 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=984 x=192 y=60 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=985 x=26 y=343 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=986 x=301 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=987 x=104 y=359 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=988 x=494 y=366 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=989 x=340 y=117 width=11 height=17 xoffset=-3 yoffset=2 xadvance=7 page=0 chnl=15 +char id=990 x=39 y=359 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=991 x=26 y=359 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=992 x=328 y=0 width=15 height=18 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=993 x=494 y=302 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=994 x=221 y=41 width=16 height=17 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=995 x=159 y=259 width=15 height=14 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=996 x=45 y=84 width=13 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=997 x=410 y=304 width=12 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=998 x=30 y=84 width=13 height=17 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=999 x=106 y=407 width=12 height=13 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=1000 x=15 y=26 width=13 height=18 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1001 x=439 y=188 width=11 height=15 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1002 x=387 y=256 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1003 x=454 y=414 width=12 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1004 x=30 y=160 width=13 height=16 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=1005 x=499 y=135 width=11 height=16 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1006 x=0 y=103 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1007 x=379 y=116 width=11 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1008 x=394 y=401 width=12 height=12 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1009 x=299 y=337 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1010 x=387 y=428 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1011 x=273 y=136 width=6 height=17 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=1012 x=416 y=272 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1013 x=448 y=400 width=10 height=12 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1014 x=460 y=398 width=10 height=12 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1015 x=286 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1016 x=405 y=116 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1017 x=15 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1018 x=451 y=254 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1019 x=252 y=321 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1020 x=332 y=190 width=12 height=15 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1021 x=345 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1022 x=0 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1023 x=135 y=291 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1024 x=418 y=116 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1025 x=28 y=122 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1026 x=0 y=65 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1027 x=481 y=116 width=10 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1028 x=120 y=294 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1029 x=438 y=302 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1030 x=430 y=384 width=5 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=1031 x=127 y=137 width=8 height=17 xoffset=-2 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=1032 x=246 y=136 width=7 height=17 xoffset=-2 yoffset=2 xadvance=5 page=0 chnl=15 +char id=1033 x=42 y=247 width=19 height=14 xoffset=-1 yoffset=2 xadvance=18 page=0 chnl=15 +char id=1034 x=261 y=241 width=17 height=14 xoffset=0 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1035 x=467 y=254 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1036 x=448 y=97 width=12 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=1037 x=441 y=78 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=1038 x=413 y=78 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1039 x=371 y=78 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1040 x=435 y=286 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1041 x=221 y=338 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1042 x=234 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1043 x=36 y=391 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1044 x=361 y=40 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1045 x=247 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1046 x=21 y=247 width=19 height=14 xoffset=-1 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1047 x=260 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1048 x=214 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1049 x=315 y=79 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=1050 x=354 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1051 x=360 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1052 x=0 y=279 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1053 x=182 y=323 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1054 x=32 y=279 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1055 x=88 y=311 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1056 x=312 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1057 x=285 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1058 x=56 y=327 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1059 x=196 y=322 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1060 x=125 y=259 width=15 height=14 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1061 x=75 y=295 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1062 x=468 y=59 width=13 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1063 x=351 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1064 x=204 y=241 width=17 height=14 xoffset=0 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1065 x=86 y=42 width=18 height=17 xoffset=0 yoffset=2 xadvance=18 page=0 chnl=15 +char id=1066 x=142 y=259 width=15 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1067 x=80 y=279 width=14 height=14 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1068 x=390 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1069 x=420 y=288 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1070 x=184 y=241 width=18 height=14 xoffset=0 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1071 x=403 y=337 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1072 x=13 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1073 x=117 y=209 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1074 x=375 y=428 width=10 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1075 x=204 y=444 width=9 height=11 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 +char id=1076 x=49 y=407 width=13 height=13 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1077 x=52 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1078 x=57 y=422 width=16 height=11 xoffset=-1 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1079 x=84 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1080 x=96 y=448 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1081 x=468 y=205 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1082 x=495 y=411 width=11 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1083 x=338 y=428 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1084 x=412 y=415 width=12 height=11 xoffset=0 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1085 x=459 y=427 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1086 x=325 y=428 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1087 x=144 y=448 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1088 x=494 y=334 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1089 x=180 y=444 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1090 x=299 y=428 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1091 x=0 y=359 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1092 x=344 y=40 width=15 height=17 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1093 x=273 y=429 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1094 x=92 y=407 width=12 height=13 xoffset=0 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1095 x=270 y=443 width=9 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1096 x=111 y=422 width=15 height=11 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=1097 x=488 y=382 width=16 height=13 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=1098 x=309 y=415 width=13 height=11 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1099 x=468 y=412 width=12 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1100 x=447 y=427 width=10 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1101 x=120 y=448 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1102 x=247 y=417 width=14 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1103 x=117 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1104 x=65 y=212 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1105 x=91 y=359 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1106 x=0 y=122 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1107 x=247 y=224 width=9 height=15 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=15 +char id=1108 x=168 y=447 width=10 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1109 x=483 y=425 width=10 height=11 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=1110 x=506 y=366 width=4 height=14 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=1111 x=291 y=385 width=8 height=14 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=1112 x=281 y=136 width=6 height=17 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=15 +char id=1113 x=75 y=422 width=16 height=11 xoffset=-1 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1114 x=196 y=418 width=15 height=11 xoffset=0 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1115 x=368 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1116 x=491 y=188 width=11 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1117 x=72 y=229 width=10 height=15 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1118 x=276 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=1119 x=184 y=403 width=10 height=13 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1120 x=375 y=240 width=16 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1121 x=162 y=418 width=15 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1122 x=160 y=275 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1123 x=228 y=305 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1124 x=290 y=173 width=16 height=15 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1125 x=408 y=401 width=12 height=12 xoffset=0 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1126 x=54 y=263 width=16 height=14 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1127 x=179 y=418 width=15 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1128 x=471 y=222 width=20 height=14 xoffset=0 yoffset=2 xadvance=19 page=0 chnl=15 +char id=1129 x=19 y=422 width=17 height=11 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=1130 x=224 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1131 x=78 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1132 x=337 y=241 width=17 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=1133 x=263 y=416 width=14 height=11 xoffset=0 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1134 x=60 y=0 width=11 height=21 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1135 x=353 y=20 width=10 height=18 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1136 x=90 y=262 width=16 height=14 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1137 x=129 y=0 width=16 height=19 xoffset=-1 yoffset=1 xadvance=14 page=0 chnl=15 +char id=1138 x=256 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1139 x=65 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1140 x=210 y=257 width=15 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1141 x=294 y=415 width=13 height=11 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1142 x=293 y=41 width=15 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=1143 x=135 y=192 width=13 height=15 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=1144 x=308 y=0 width=18 height=18 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=1145 x=218 y=173 width=16 height=15 xoffset=-1 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1146 x=124 y=175 width=17 height=15 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1147 x=306 y=401 width=14 height=12 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1148 x=22 y=46 width=20 height=17 xoffset=-1 yoffset=-1 xadvance=19 page=0 chnl=15 +char id=1149 x=162 y=173 width=17 height=15 xoffset=0 yoffset=1 xadvance=16 page=0 chnl=15 +char id=1150 x=275 y=41 width=16 height=17 xoffset=-1 yoffset=-1 xadvance=15 page=0 chnl=15 +char id=1151 x=193 y=257 width=15 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1152 x=378 y=59 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1153 x=242 y=369 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1154 x=72 y=448 width=10 height=11 xoffset=-1 yoffset=6 xadvance=8 page=0 chnl=15 +char id=1155 x=319 y=453 width=10 height=6 xoffset=-10 yoffset=1 xadvance=0 page=0 chnl=15 +char id=1156 x=481 y=448 width=9 height=5 xoffset=-7 yoffset=1 xadvance=0 page=0 chnl=15 +char id=1157 x=453 y=452 width=5 height=6 xoffset=-6 yoffset=1 xadvance=0 page=0 chnl=15 +char id=1158 x=467 y=450 width=5 height=6 xoffset=-6 yoffset=1 xadvance=0 page=0 chnl=15 +char id=1159 x=233 y=457 width=16 height=6 xoffset=-14 yoffset=1 xadvance=0 page=0 chnl=15 +char id=1160 x=73 y=0 width=26 height=20 xoffset=-18 yoffset=-1 xadvance=7 page=0 chnl=15 +char id=1161 x=0 y=0 width=24 height=24 xoffset=-17 yoffset=-3 xadvance=7 page=0 chnl=15 +char id=1162 x=45 y=0 width=13 height=21 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=1163 x=228 y=0 width=12 height=19 xoffset=0 yoffset=1 xadvance=11 page=0 chnl=15 +char id=1164 x=172 y=307 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1165 x=133 y=406 width=11 height=13 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=1166 x=156 y=355 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1167 x=182 y=207 width=11 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1168 x=357 y=154 width=10 height=16 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=1169 x=207 y=403 width=9 height=13 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=15 +char id=1170 x=424 y=304 width=12 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1171 x=104 y=435 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1172 x=314 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1173 x=204 y=387 width=10 height=14 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 +char id=1174 x=0 y=46 width=20 height=17 xoffset=-1 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1175 x=72 y=263 width=16 height=14 xoffset=-1 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1176 x=275 y=117 width=11 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1177 x=96 y=391 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1178 x=348 y=59 width=13 height=17 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1179 x=65 y=359 width=11 height=14 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1180 x=98 y=327 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1181 x=156 y=434 width=11 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1182 x=240 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1183 x=276 y=190 width=12 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1184 x=0 y=263 width=16 height=14 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1185 x=279 y=415 width=13 height=11 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1186 x=333 y=59 width=13 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1187 x=78 y=407 width=12 height=13 xoffset=0 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1188 x=84 y=246 width=18 height=14 xoffset=0 yoffset=2 xadvance=16 page=0 chnl=15 +char id=1189 x=230 y=417 width=15 height=11 xoffset=0 yoffset=5 xadvance=14 page=0 chnl=15 +char id=1190 x=106 y=42 width=18 height=17 xoffset=0 yoffset=2 xadvance=17 page=0 chnl=15 +char id=1191 x=465 y=238 width=16 height=14 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=1192 x=272 y=173 width=16 height=15 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1193 x=322 y=401 width=13 height=12 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1194 x=303 y=60 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1195 x=398 y=369 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1196 x=498 y=58 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1197 x=159 y=403 width=11 height=13 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1198 x=28 y=327 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1199 x=481 y=334 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1200 x=308 y=321 width=12 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1201 x=130 y=209 width=11 height=15 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1202 x=318 y=60 width=13 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1203 x=455 y=334 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1204 x=184 y=41 width=17 height=17 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1205 x=0 y=407 width=15 height=13 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=1206 x=343 y=78 width=12 height=17 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1207 x=120 y=407 width=11 height=13 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1208 x=429 y=336 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1209 x=248 y=443 width=9 height=11 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1210 x=416 y=336 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1211 x=206 y=371 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1212 x=242 y=241 width=17 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1213 x=354 y=415 width=13 height=11 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1214 x=165 y=41 width=17 height=17 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1215 x=180 y=290 width=13 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1216 x=444 y=384 width=5 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=1217 x=44 y=43 width=19 height=17 xoffset=-1 yoffset=-1 xadvance=17 page=0 chnl=15 +char id=1218 x=200 y=173 width=16 height=15 xoffset=-1 yoffset=1 xadvance=14 page=0 chnl=15 +char id=1219 x=74 y=22 width=12 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1220 x=91 y=212 width=11 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1221 x=442 y=0 width=14 height=18 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1222 x=60 y=195 width=13 height=15 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1223 x=469 y=78 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1224 x=13 y=359 width=11 height=14 xoffset=0 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1225 x=473 y=0 width=13 height=18 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1226 x=150 y=191 width=12 height=15 xoffset=0 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1227 x=392 y=116 width=11 height=17 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1228 x=196 y=403 width=9 height=13 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1229 x=345 y=0 width=15 height=18 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1230 x=45 y=196 width=13 height=15 xoffset=0 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1231 x=470 y=382 width=4 height=14 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15 +char id=1232 x=30 y=23 width=13 height=18 xoffset=-1 yoffset=-2 xadvance=11 page=0 chnl=15 +char id=1233 x=465 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1234 x=15 y=84 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=1235 x=442 y=352 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1236 x=280 y=241 width=17 height=14 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=1237 x=38 y=422 width=17 height=11 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=1238 x=366 y=116 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1239 x=413 y=188 width=11 height=15 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1240 x=227 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1241 x=182 y=431 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1242 x=256 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=1243 x=195 y=339 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1244 x=65 y=43 width=19 height=17 xoffset=-1 yoffset=-1 xadvance=17 page=0 chnl=15 +char id=1245 x=393 y=240 width=16 height=14 xoffset=-1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1246 x=249 y=117 width=11 height=17 xoffset=0 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1247 x=120 y=391 width=10 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1248 x=156 y=339 width=11 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1249 x=117 y=343 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1250 x=187 y=155 width=12 height=16 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 +char id=1251 x=132 y=390 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1252 x=266 y=98 width=12 height=17 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=1253 x=254 y=369 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1254 x=224 y=60 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=1255 x=52 y=343 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1256 x=464 y=270 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1257 x=482 y=412 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1258 x=393 y=40 width=14 height=17 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=1259 x=39 y=343 width=11 height=14 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1260 x=120 y=80 width=13 height=17 xoffset=-1 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=1261 x=0 y=391 width=10 height=14 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1262 x=131 y=156 width=12 height=16 xoffset=-1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=1263 x=184 y=117 width=11 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1264 x=140 y=98 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1265 x=93 y=119 width=11 height=17 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1266 x=28 y=103 width=12 height=17 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=1267 x=198 y=21 width=11 height=18 xoffset=-1 yoffset=1 xadvance=9 page=0 chnl=15 +char id=1268 x=145 y=117 width=11 height=17 xoffset=0 yoffset=-1 xadvance=11 page=0 chnl=15 +char id=1269 x=239 y=386 width=9 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1270 x=469 y=116 width=10 height=17 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1271 x=218 y=403 width=9 height=13 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 +char id=1272 x=240 y=60 width=14 height=17 xoffset=0 yoffset=-1 xadvance=14 page=0 chnl=15 +char id=1273 x=382 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1274 x=252 y=98 width=12 height=17 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1275 x=499 y=254 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1276 x=45 y=23 width=13 height=18 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1277 x=208 y=207 width=11 height=15 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1278 x=300 y=289 width=13 height=14 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1279 x=247 y=430 width=11 height=11 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1329 x=248 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1330 x=256 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1331 x=483 y=254 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1332 x=371 y=256 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1333 x=206 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1334 x=45 y=295 width=13 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1335 x=377 y=353 width=11 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1336 x=186 y=306 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1337 x=377 y=171 width=15 height=15 xoffset=0 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1338 x=477 y=171 width=14 height=15 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1339 x=154 y=323 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1340 x=182 y=371 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1341 x=360 y=172 width=15 height=15 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=1342 x=493 y=171 width=13 height=15 xoffset=1 yoffset=2 xadvance=14 page=0 chnl=15 +char id=1343 x=84 y=327 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1344 x=120 y=192 width=13 height=15 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1345 x=290 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1346 x=339 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1347 x=323 y=257 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1348 x=461 y=171 width=14 height=15 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1349 x=262 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1350 x=75 y=195 width=13 height=15 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1351 x=304 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1352 x=284 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1353 x=0 y=196 width=13 height=15 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1354 x=272 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1355 x=452 y=302 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1356 x=304 y=273 width=14 height=14 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1357 x=312 y=305 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1358 x=336 y=273 width=14 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1359 x=318 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1360 x=280 y=321 width=12 height=14 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=1361 x=220 y=190 width=12 height=15 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1362 x=470 y=366 width=10 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=1363 x=435 y=254 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1364 x=0 y=311 width=13 height=14 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=1365 x=384 y=272 width=14 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1366 x=445 y=171 width=14 height=15 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=1369 x=19 y=461 width=6 height=8 xoffset=-1 yoffset=1 xadvance=5 page=0 chnl=15 +char id=1370 x=135 y=461 width=5 height=7 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=1371 x=415 y=453 width=6 height=6 xoffset=-1 yoffset=1 xadvance=4 page=0 chnl=15 +char id=1372 x=487 y=438 width=8 height=8 xoffset=-1 yoffset=-1 xadvance=6 page=0 chnl=15 +char id=1373 x=371 y=453 width=7 height=6 xoffset=-1 yoffset=1 xadvance=4 page=0 chnl=15 +char id=1374 x=71 y=461 width=9 height=7 xoffset=-1 yoffset=0 xadvance=6 page=0 chnl=15 +char id=1375 x=307 y=454 width=10 height=6 xoffset=-1 yoffset=1 xadvance=8 page=0 chnl=15 +char id=1377 x=288 y=401 width=16 height=12 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=1378 x=420 y=205 width=10 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1379 x=15 y=196 width=13 height=15 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1380 x=178 y=190 width=12 height=15 xoffset=0 yoffset=5 xadvance=11 page=0 chnl=15 +char id=1381 x=429 y=153 width=10 height=16 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1382 x=30 y=196 width=13 height=15 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1383 x=258 y=224 width=9 height=15 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=15 +char id=1384 x=168 y=225 width=10 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1385 x=105 y=192 width=13 height=15 xoffset=0 yoffset=5 xadvance=12 page=0 chnl=15 +char id=1386 x=0 y=160 width=13 height=16 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=1387 x=296 y=0 width=10 height=19 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1388 x=504 y=188 width=6 height=15 xoffset=0 yoffset=5 xadvance=4 page=0 chnl=15 +char id=1389 x=111 y=0 width=16 height=19 xoffset=0 yoffset=1 xadvance=16 page=0 chnl=15 +char id=1390 x=241 y=155 width=11 height=16 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1391 x=284 y=0 width=10 height=19 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1392 x=144 y=390 width=10 height=14 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=1393 x=228 y=155 width=11 height=16 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1394 x=360 y=189 width=12 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1395 x=441 y=153 width=10 height=16 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1396 x=117 y=157 width=12 height=16 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1397 x=504 y=205 width=6 height=15 xoffset=-2 yoffset=5 xadvance=4 page=0 chnl=15 +char id=1398 x=103 y=157 width=12 height=16 xoffset=-2 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1399 x=269 y=224 width=9 height=15 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=1400 x=108 y=448 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1401 x=341 y=224 width=8 height=15 xoffset=-1 yoffset=5 xadvance=6 page=0 chnl=15 +char id=1402 x=254 y=173 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=1403 x=324 y=207 width=10 height=15 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=1404 x=208 y=431 width=11 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1405 x=156 y=447 width=10 height=11 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1406 x=214 y=0 width=12 height=19 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=1407 x=270 y=401 width=16 height=12 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=1408 x=408 y=205 width=10 height=15 xoffset=0 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1409 x=13 y=375 width=11 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1410 x=281 y=442 width=9 height=11 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15 +char id=1411 x=147 y=0 width=16 height=19 xoffset=0 yoffset=1 xadvance=16 page=0 chnl=15 +char id=1412 x=164 y=190 width=12 height=15 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1413 x=234 y=430 width=11 height=11 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=1414 x=165 y=0 width=15 height=19 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=1415 x=403 y=135 width=14 height=16 xoffset=0 yoffset=1 xadvance=13 page=0 chnl=15 +char id=1417 x=455 y=440 width=5 height=9 xoffset=0 yoffset=7 xadvance=5 page=0 chnl=15 +char id=1418 x=164 y=468 width=7 height=4 xoffset=-1 yoffset=9 xadvance=6 page=0 chnl=15 +char id=8192 x=289 y=463 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=8 page=0 chnl=15 +char id=8193 x=507 y=349 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=16 page=0 chnl=15 +char id=8194 x=507 y=334 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=8 page=0 chnl=15 +char id=8195 x=507 y=339 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=16 page=0 chnl=15 +char id=8196 x=249 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=8197 x=244 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=4 page=0 chnl=15 +char id=8198 x=214 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=3 page=0 chnl=15 +char id=8199 x=507 y=439 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=10 page=0 chnl=15 +char id=8200 x=507 y=424 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=5 page=0 chnl=15 +char id=8201 x=219 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=3 page=0 chnl=15 +char id=8202 x=224 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=2 page=0 chnl=15 +char id=8203 x=234 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8204 x=239 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8205 x=264 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8206 x=269 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8207 x=274 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8208 x=182 y=468 width=7 height=4 xoffset=-1 yoffset=9 xadvance=6 page=0 chnl=15 +char id=8209 x=173 y=468 width=7 height=4 xoffset=-1 yoffset=9 xadvance=6 page=0 chnl=15 +char id=8210 x=79 y=470 width=12 height=4 xoffset=-1 yoffset=9 xadvance=10 page=0 chnl=15 +char id=8211 x=121 y=470 width=10 height=4 xoffset=-1 yoffset=9 xadvance=8 page=0 chnl=15 +char id=8212 x=21 y=471 width=18 height=4 xoffset=-1 yoffset=9 xadvance=16 page=0 chnl=15 +char id=8213 x=41 y=470 width=18 height=4 xoffset=-1 yoffset=9 xadvance=16 page=0 chnl=15 +char id=8214 x=413 y=20 width=8 height=18 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8215 x=57 y=461 width=12 height=7 xoffset=-2 yoffset=13 xadvance=8 page=0 chnl=15 +char id=8216 x=142 y=461 width=5 height=7 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=8217 x=128 y=461 width=5 height=7 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=8218 x=460 y=451 width=5 height=6 xoffset=0 yoffset=12 xadvance=5 page=0 chnl=15 +char id=8219 x=149 y=461 width=5 height=7 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=8220 x=82 y=461 width=8 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8221 x=92 y=461 width=8 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8222 x=361 y=453 width=8 height=6 xoffset=0 yoffset=12 xadvance=8 page=0 chnl=15 +char id=8223 x=102 y=461 width=8 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8224 x=417 y=153 width=10 height=16 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8225 x=405 y=153 width=10 height=16 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8226 x=10 y=461 width=7 height=8 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=15 +char id=8227 x=445 y=441 width=8 height=9 xoffset=1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=8228 x=200 y=465 width=5 height=4 xoffset=0 yoffset=12 xadvance=5 page=0 chnl=15 +char id=8229 x=133 y=470 width=10 height=4 xoffset=0 yoffset=12 xadvance=11 page=0 chnl=15 +char id=8230 x=61 y=470 width=16 height=4 xoffset=0 yoffset=12 xadvance=16 page=0 chnl=15 +char id=8231 x=7 y=471 width=5 height=5 xoffset=0 yoffset=7 xadvance=5 page=0 chnl=15 +char id=8232 x=507 y=359 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8233 x=229 y=465 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8234 x=254 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8235 x=507 y=434 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8236 x=507 y=429 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8237 x=279 y=464 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8238 x=507 y=354 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8239 x=507 y=344 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=3 page=0 chnl=15 +char id=8240 x=423 y=222 width=23 height=14 xoffset=-1 yoffset=2 xadvance=21 page=0 chnl=15 +char id=8241 x=49 y=178 width=29 height=15 xoffset=-1 yoffset=2 xadvance=28 page=0 chnl=15 +char id=8242 x=407 y=453 width=6 height=6 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15 +char id=8243 x=331 y=453 width=8 height=6 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=8244 x=294 y=454 width=11 height=6 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8245 x=431 y=453 width=6 height=6 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15 +char id=8246 x=351 y=453 width=8 height=6 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=8247 x=281 y=455 width=11 height=6 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8248 x=341 y=453 width=8 height=6 xoffset=-1 yoffset=14 xadvance=5 page=0 chnl=15 +char id=8249 x=420 y=441 width=6 height=10 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 +char id=8250 x=402 y=441 width=7 height=10 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=15 +char id=8251 x=45 y=311 width=13 height=14 xoffset=0 yoffset=2 xadvance=13 page=0 chnl=15 +char id=8252 x=351 y=385 width=8 height=14 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8253 x=250 y=385 width=9 height=14 xoffset=0 yoffset=2 xadvance=9 page=0 chnl=15 +char id=8254 x=93 y=470 width=12 height=4 xoffset=-2 yoffset=2 xadvance=8 page=0 chnl=15 +char id=8255 x=214 y=457 width=17 height=6 xoffset=-2 yoffset=14 xadvance=13 page=0 chnl=15 +char id=8256 x=176 y=460 width=17 height=6 xoffset=-2 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=8257 x=372 y=441 width=8 height=10 xoffset=-2 yoffset=10 xadvance=4 page=0 chnl=15 +char id=8258 x=329 y=136 width=18 height=16 xoffset=-1 yoffset=1 xadvance=16 page=0 chnl=15 +char id=8259 x=492 y=448 width=8 height=5 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=15 +char id=8260 x=117 y=375 width=11 height=14 xoffset=-4 yoffset=2 xadvance=3 page=0 chnl=15 +char id=8261 x=503 y=0 width=6 height=18 xoffset=0 yoffset=1 xadvance=6 page=0 chnl=15 +char id=8262 x=453 y=20 width=6 height=18 xoffset=0 yoffset=1 xadvance=6 page=0 chnl=15 +char id=8263 x=223 y=241 width=17 height=14 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=8264 x=448 y=318 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=8265 x=462 y=318 width=12 height=14 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=8266 x=172 y=403 width=10 height=13 xoffset=-1 yoffset=5 xadvance=8 page=0 chnl=15 +char id=8267 x=280 y=155 width=11 height=16 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 +char id=8268 x=497 y=437 width=8 height=8 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 +char id=8269 x=477 y=438 width=8 height=8 xoffset=0 yoffset=5 xadvance=8 page=0 chnl=15 +char id=8270 x=325 y=441 width=10 height=10 xoffset=-1 yoffset=7 xadvance=8 page=0 chnl=15 +char id=8271 x=263 y=401 width=5 height=13 xoffset=1 yoffset=5 xadvance=5 page=0 chnl=15 +char id=8272 x=26 y=0 width=17 height=21 xoffset=-2 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=8273 x=389 y=20 width=10 height=18 xoffset=-1 yoffset=-1 xadvance=8 page=0 chnl=15 +char id=8274 x=280 y=224 width=9 height=15 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=15 +char id=8275 x=156 y=460 width=18 height=6 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=8276 x=195 y=457 width=17 height=6 xoffset=-2 yoffset=14 xadvance=13 page=0 chnl=15 +char id=8277 x=435 y=400 width=11 height=12 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=15 +char id=8278 x=361 y=441 width=9 height=10 xoffset=0 yoffset=4 xadvance=9 page=0 chnl=15 +char id=8279 x=251 y=456 width=13 height=6 xoffset=-1 yoffset=2 xadvance=11 page=0 chnl=15 +char id=8280 x=26 y=435 width=11 height=11 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=8281 x=39 y=435 width=11 height=11 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=8282 x=451 y=384 width=5 height=14 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15 +char id=8283 x=198 y=0 width=14 height=19 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=8284 x=176 y=258 width=15 height=14 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=8285 x=256 y=401 width=5 height=13 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 +char id=8286 x=249 y=402 width=5 height=13 xoffset=0 yoffset=3 xadvance=5 page=0 chnl=15 +char id=8287 x=294 y=462 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=4 page=0 chnl=15 +char id=8288 x=299 y=462 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8289 x=304 y=462 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8290 x=309 y=462 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8291 x=314 y=462 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8292 x=319 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8298 x=324 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8299 x=329 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8300 x=334 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8301 x=339 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8302 x=344 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 +char id=8303 x=349 y=461 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=0 page=0 chnl=15 diff --git a/examples/text/resources/dejavu.png b/examples/text/resources/dejavu.png new file mode 100644 index 00000000..948c36d6 Binary files /dev/null and b/examples/text/resources/dejavu.png differ diff --git a/examples/text/resources/emoji.fnt b/examples/text/resources/emoji.fnt new file mode 100644 index 00000000..cabbdf6c --- /dev/null +++ b/examples/text/resources/emoji.fnt @@ -0,0 +1,191 @@ +info face="Symbola" size=-64 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 outline=2 +common lineHeight=81 base=59 scaleW=1024 scaleH=1024 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 +page id=0 file="emoji_0.png" +chars count=187 +char id=9749 x=135 y=333 width=63 height=61 xoffset=1 yoffset=9 xadvance=65 page=0 chnl=15 +char id=9752 x=366 y=396 width=57 height=59 xoffset=0 yoffset=10 xadvance=58 page=0 chnl=15 +char id=9760 x=257 y=0 width=46 height=68 xoffset=0 yoffset=7 xadvance=47 page=0 chnl=15 +char id=9785 x=61 y=579 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=9786 x=183 y=578 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=10083 x=984 y=200 width=35 height=50 xoffset=1 yoffset=12 xadvance=37 page=0 chnl=15 +char id=10084 x=266 y=697 width=58 height=44 xoffset=1 yoffset=15 xadvance=60 page=0 chnl=15 +char id=127744 x=736 y=266 width=40 height=63 xoffset=4 yoffset=10 xadvance=48 page=0 chnl=15 +char id=127789 x=401 y=637 width=64 height=56 xoffset=0 yoffset=14 xadvance=64 page=0 chnl=15 +char id=127790 x=697 y=331 width=62 height=60 xoffset=1 yoffset=10 xadvance=64 page=0 chnl=15 +char id=127791 x=0 y=699 width=64 height=51 xoffset=0 yoffset=17 xadvance=64 page=0 chnl=15 +char id=127792 x=456 y=202 width=64 height=63 xoffset=0 yoffset=9 xadvance=64 page=0 chnl=15 +char id=127798 x=523 y=695 width=58 height=35 xoffset=3 yoffset=27 xadvance=64 page=0 chnl=15 +char id=127805 x=825 y=330 width=60 height=60 xoffset=0 yoffset=11 xadvance=60 page=0 chnl=15 +char id=127806 x=259 y=268 width=60 height=63 xoffset=0 yoffset=10 xadvance=60 page=0 chnl=15 +char id=127807 x=322 y=333 width=59 height=61 xoffset=0 yoffset=10 xadvance=60 page=0 chnl=15 +char id=127808 x=0 y=269 width=63 height=63 xoffset=0 yoffset=9 xadvance=63 page=0 chnl=15 +char id=127811 x=305 y=396 width=59 height=59 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=127812 x=696 y=0 width=64 height=66 xoffset=2 yoffset=8 xadvance=68 page=0 chnl=15 +char id=127813 x=61 y=397 width=58 height=60 xoffset=0 yoffset=11 xadvance=58 page=0 chnl=15 +char id=127814 x=946 y=265 width=61 height=62 xoffset=0 yoffset=9 xadvance=61 page=0 chnl=15 +char id=127815 x=584 y=635 width=56 height=56 xoffset=0 yoffset=12 xadvance=56 page=0 chnl=15 +char id=127827 x=121 y=397 width=58 height=60 xoffset=0 yoffset=10 xadvance=58 page=0 chnl=15 +char id=127828 x=0 y=334 width=68 height=61 xoffset=0 yoffset=10 xadvance=68 page=0 chnl=15 +char id=127829 x=68 y=70 width=65 height=65 xoffset=0 yoffset=8 xadvance=65 page=0 chnl=15 +char id=127830 x=737 y=135 width=59 height=64 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=127831 x=560 y=0 width=66 height=66 xoffset=0 yoffset=8 xadvance=66 page=0 chnl=15 +char id=127832 x=135 y=70 width=65 height=65 xoffset=0 yoffset=8 xadvance=65 page=0 chnl=15 +char id=127837 x=630 y=331 width=65 height=60 xoffset=0 yoffset=13 xadvance=65 page=0 chnl=15 +char id=127838 x=628 y=0 width=66 height=66 xoffset=0 yoffset=8 xadvance=66 page=0 chnl=15 +char id=127839 x=566 y=267 width=59 height=63 xoffset=0 yoffset=9 xadvance=59 page=0 chnl=15 +char id=127840 x=562 y=332 width=66 height=60 xoffset=0 yoffset=12 xadvance=66 page=0 chnl=15 +char id=127841 x=798 y=135 width=57 height=64 xoffset=0 yoffset=9 xadvance=57 page=0 chnl=15 +char id=127842 x=467 y=637 width=57 height=56 xoffset=0 yoffset=13 xadvance=57 page=0 chnl=15 +char id=127845 x=213 y=137 width=65 height=64 xoffset=0 yoffset=9 xadvance=65 page=0 chnl=15 +char id=127846 x=950 y=67 width=34 height=65 xoffset=0 yoffset=8 xadvance=34 page=0 chnl=15 +char id=127850 x=522 y=202 width=64 height=63 xoffset=0 yoffset=9 xadvance=64 page=0 chnl=15 +char id=127851 x=544 y=135 width=64 height=64 xoffset=0 yoffset=9 xadvance=65 page=0 chnl=15 +char id=127855 x=440 y=333 width=49 height=61 xoffset=0 yoffset=10 xadvance=49 page=0 chnl=15 +char id=127856 x=207 y=638 width=64 height=57 xoffset=0 yoffset=11 xadvance=64 page=0 chnl=15 +char id=127857 x=710 y=633 width=65 height=55 xoffset=0 yoffset=13 xadvance=66 page=0 chnl=15 +char id=127858 x=237 y=396 width=66 height=59 xoffset=0 yoffset=12 xadvance=66 page=0 chnl=15 +char id=127859 x=195 y=268 width=62 height=63 xoffset=1 yoffset=9 xadvance=64 page=0 chnl=15 +char id=127861 x=77 y=0 width=65 height=68 xoffset=0 yoffset=6 xadvance=65 page=0 chnl=15 +char id=127862 x=857 y=135 width=57 height=64 xoffset=0 yoffset=9 xadvance=58 page=0 chnl=15 +char id=127863 x=47 y=203 width=41 height=64 xoffset=0 yoffset=9 xadvance=41 page=0 chnl=15 +char id=127867 x=833 y=633 width=66 height=54 xoffset=0 yoffset=12 xadvance=66 page=0 chnl=15 +char id=127868 x=986 y=67 width=25 height=53 xoffset=0 yoffset=12 xadvance=25 page=0 chnl=15 +char id=127870 x=383 y=333 width=55 height=61 xoffset=1 yoffset=10 xadvance=57 page=0 chnl=15 +char id=127871 x=530 y=394 width=42 height=59 xoffset=0 yoffset=12 xadvance=42 page=0 chnl=15 +char id=127874 x=0 y=70 width=66 height=65 xoffset=0 yoffset=8 xadvance=66 page=0 chnl=15 +char id=128065 x=384 y=696 width=68 height=39 xoffset=-2 yoffset=19 xadvance=64 page=0 chnl=15 +char id=128068 x=132 y=698 width=57 height=47 xoffset=0 yoffset=15 xadvance=57 page=0 chnl=15 +char id=128069 x=777 y=633 width=54 height=55 xoffset=0 yoffset=14 xadvance=54 page=0 chnl=15 +char id=128121 x=478 y=136 width=64 height=64 xoffset=0 yoffset=8 xadvance=64 page=0 chnl=15 +char id=128122 x=65 y=269 width=63 height=63 xoffset=0 yoffset=9 xadvance=63 page=0 chnl=15 +char id=128123 x=339 y=637 width=60 height=57 xoffset=2 yoffset=14 xadvance=64 page=0 chnl=15 +char id=128125 x=505 y=267 width=59 height=63 xoffset=0 yoffset=9 xadvance=60 page=0 chnl=15 +char id=128126 x=321 y=268 width=60 height=63 xoffset=2 yoffset=7 xadvance=64 page=0 chnl=15 +char id=128127 x=889 y=67 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128128 x=627 y=266 width=55 height=63 xoffset=0 yoffset=9 xadvance=55 page=0 chnl=15 +char id=128139 x=574 y=394 width=68 height=58 xoffset=0 yoffset=12 xadvance=68 page=0 chnl=15 +char id=128140 x=202 y=70 width=65 height=65 xoffset=0 yoffset=8 xadvance=65 page=0 chnl=15 +char id=128147 x=642 y=634 width=66 height=55 xoffset=0 yoffset=12 xadvance=66 page=0 chnl=15 +char id=128151 x=588 y=201 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=128152 x=280 y=137 width=64 height=64 xoffset=0 yoffset=9 xadvance=64 page=0 chnl=15 +char id=128153 x=654 y=201 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=128155 x=720 y=201 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=128156 x=786 y=201 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=128157 x=77 y=137 width=67 height=64 xoffset=0 yoffset=9 xadvance=68 page=0 chnl=15 +char id=128159 x=335 y=69 width=64 height=65 xoffset=0 yoffset=8 xadvance=64 page=0 chnl=15 +char id=128162 x=130 y=268 width=63 height=63 xoffset=0 yoffset=9 xadvance=63 page=0 chnl=15 +char id=128163 x=146 y=137 width=65 height=64 xoffset=0 yoffset=9 xadvance=65 page=0 chnl=15 +char id=128164 x=454 y=695 width=67 height=38 xoffset=0 yoffset=23 xadvance=67 page=0 chnl=15 +char id=128169 x=916 y=134 width=57 height=64 xoffset=0 yoffset=9 xadvance=58 page=0 chnl=15 +char id=128420 x=852 y=201 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=128512 x=732 y=513 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128514 x=644 y=393 width=63 height=58 xoffset=0 yoffset=12 xadvance=63 page=0 chnl=15 +char id=128515 x=610 y=514 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128518 x=488 y=517 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128519 x=144 y=0 width=59 height=68 xoffset=0 yoffset=7 xadvance=59 page=0 chnl=15 +char id=128520 x=584 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128521 x=427 y=517 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128523 x=61 y=519 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128524 x=0 y=519 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128525 x=915 y=452 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128526 x=854 y=452 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128527 x=793 y=453 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128528 x=732 y=453 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128529 x=671 y=453 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128530 x=488 y=457 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128533 x=122 y=459 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128534 x=854 y=572 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128535 x=305 y=457 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128536 x=122 y=579 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128537 x=366 y=517 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128538 x=244 y=577 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128539 x=305 y=577 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128541 x=366 y=577 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128542 x=427 y=577 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128543 x=488 y=577 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128544 x=549 y=575 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128545 x=671 y=573 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128546 x=732 y=573 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128547 x=793 y=573 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128548 x=427 y=0 width=59 height=67 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128549 x=915 y=572 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128550 x=0 y=639 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128553 x=898 y=392 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128554 x=959 y=391 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128555 x=0 y=459 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128556 x=61 y=459 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128557 x=305 y=0 width=59 height=67 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128558 x=183 y=458 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128559 x=244 y=457 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128560 x=709 y=393 width=61 height=58 xoffset=0 yoffset=12 xadvance=62 page=0 chnl=15 +char id=128561 x=835 y=392 width=61 height=58 xoffset=0 yoffset=12 xadvance=61 page=0 chnl=15 +char id=128562 x=366 y=457 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128563 x=427 y=457 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128564 x=90 y=203 width=88 height=63 xoffset=0 yoffset=7 xadvance=59 page=0 chnl=15 +char id=128565 x=549 y=455 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128566 x=610 y=574 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128567 x=610 y=454 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128568 x=401 y=69 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128569 x=878 y=0 width=67 height=65 xoffset=0 yoffset=8 xadvance=67 page=0 chnl=15 +char id=128570 x=523 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128571 x=828 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128573 x=767 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128575 x=706 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128576 x=645 y=68 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=128577 x=122 y=519 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128578 x=183 y=518 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128579 x=244 y=517 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=128580 x=305 y=517 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129296 x=200 y=333 width=59 height=61 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129297 x=366 y=0 width=59 height=67 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=129298 x=383 y=268 width=59 height=63 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129299 x=549 y=515 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129300 x=0 y=0 width=75 height=68 xoffset=0 yoffset=7 xadvance=72 page=0 chnl=15 +char id=129301 x=671 y=513 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129302 x=181 y=396 width=54 height=60 xoffset=2 yoffset=7 xadvance=59 page=0 chnl=15 +char id=129303 x=778 y=266 width=98 height=62 xoffset=-1 yoffset=12 xadvance=96 page=0 chnl=15 +char id=129312 x=0 y=137 width=75 height=64 xoffset=-1 yoffset=9 xadvance=74 page=0 chnl=15 +char id=129314 x=793 y=513 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129315 x=346 y=136 width=64 height=64 xoffset=-1 yoffset=9 xadvance=64 page=0 chnl=15 +char id=129316 x=444 y=267 width=59 height=63 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129319 x=488 y=0 width=70 height=66 xoffset=0 yoffset=8 xadvance=70 page=0 chnl=15 +char id=129320 x=854 y=512 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129321 x=915 y=512 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129322 x=0 y=579 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129323 x=762 y=0 width=59 height=66 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=129324 x=772 y=393 width=61 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129325 x=949 y=329 width=59 height=60 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129326 x=261 y=333 width=59 height=61 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129327 x=462 y=69 width=59 height=65 xoffset=0 yoffset=8 xadvance=59 page=0 chnl=15 +char id=129346 x=975 y=134 width=46 height=64 xoffset=-1 yoffset=8 xadvance=44 page=0 chnl=15 +char id=129347 x=675 y=135 width=60 height=64 xoffset=-1 yoffset=9 xadvance=58 page=0 chnl=15 +char id=129360 x=70 y=334 width=63 height=61 xoffset=-1 yoffset=10 xadvance=61 page=0 chnl=15 +char id=129361 x=191 y=698 width=73 height=45 xoffset=-1 yoffset=18 xadvance=72 page=0 chnl=15 +char id=129362 x=253 y=203 width=66 height=63 xoffset=-1 yoffset=9 xadvance=64 page=0 chnl=15 +char id=129363 x=122 y=639 width=83 height=57 xoffset=-1 yoffset=14 xadvance=82 page=0 chnl=15 +char id=129364 x=491 y=332 width=69 height=60 xoffset=-1 yoffset=11 xadvance=68 page=0 chnl=15 +char id=129365 x=412 y=136 width=64 height=64 xoffset=-1 yoffset=8 xadvance=63 page=0 chnl=15 +char id=129366 x=947 y=0 width=66 height=65 xoffset=-1 yoffset=8 xadvance=65 page=0 chnl=15 +char id=129367 x=180 y=203 width=71 height=63 xoffset=-1 yoffset=8 xadvance=70 page=0 chnl=15 +char id=129368 x=878 y=266 width=66 height=62 xoffset=-1 yoffset=9 xadvance=65 page=0 chnl=15 +char id=129369 x=269 y=70 width=64 height=65 xoffset=-1 yoffset=8 xadvance=63 page=0 chnl=15 +char id=129370 x=823 y=0 width=53 height=66 xoffset=1 yoffset=8 xadvance=55 page=0 chnl=15 +char id=129371 x=205 y=0 width=50 height=68 xoffset=1 yoffset=7 xadvance=52 page=0 chnl=15 +char id=129372 x=389 y=202 width=65 height=63 xoffset=1 yoffset=9 xadvance=67 page=0 chnl=15 +char id=129373 x=887 y=330 width=60 height=60 xoffset=1 yoffset=10 xadvance=62 page=0 chnl=15 +char id=129374 x=961 y=632 width=61 height=51 xoffset=1 yoffset=15 xadvance=64 page=0 chnl=15 +char id=129375 x=526 y=637 width=56 height=56 xoffset=1 yoffset=12 xadvance=58 page=0 chnl=15 +char id=129377 x=425 y=396 width=53 height=59 xoffset=1 yoffset=10 xadvance=56 page=0 chnl=15 +char id=129378 x=321 y=203 width=66 height=63 xoffset=-1 yoffset=9 xadvance=64 page=0 chnl=15 +char id=129379 x=761 y=331 width=62 height=60 xoffset=1 yoffset=12 xadvance=64 page=0 chnl=15 +char id=129380 x=480 y=396 width=48 height=59 xoffset=4 yoffset=13 xadvance=56 page=0 chnl=15 +char id=129381 x=0 y=397 width=59 height=60 xoffset=0 yoffset=10 xadvance=59 page=0 chnl=15 +char id=129382 x=684 y=266 width=50 height=63 xoffset=1 yoffset=10 xadvance=53 page=0 chnl=15 +char id=129383 x=610 y=135 width=63 height=64 xoffset=1 yoffset=8 xadvance=65 page=0 chnl=15 +char id=129384 x=901 y=632 width=58 height=53 xoffset=2 yoffset=16 xadvance=63 page=0 chnl=15 +char id=129385 x=326 y=696 width=56 height=41 xoffset=2 yoffset=19 xadvance=60 page=0 chnl=15 +char id=129386 x=273 y=637 width=64 height=57 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 +char id=129387 x=0 y=203 width=45 height=64 xoffset=4 yoffset=9 xadvance=53 page=0 chnl=15 +char id=129472 x=66 y=699 width=64 height=49 xoffset=0 yoffset=14 xadvance=64 page=0 chnl=15 +char id=129488 x=61 y=639 width=59 height=58 xoffset=0 yoffset=12 xadvance=59 page=0 chnl=15 +char id=129505 x=918 y=200 width=64 height=63 xoffset=0 yoffset=10 xadvance=64 page=0 chnl=15 diff --git a/examples/text/resources/emoji_0.png b/examples/text/resources/emoji_0.png new file mode 100644 index 00000000..82e36983 Binary files /dev/null and b/examples/text/resources/emoji_0.png differ diff --git a/examples/text/resources/notoCJK.fnt b/examples/text/resources/notoCJK.fnt new file mode 100644 index 00000000..235ee771 --- /dev/null +++ b/examples/text/resources/notoCJK.fnt @@ -0,0 +1,580 @@ +info face="Noto Serif CJK JP" size=-16 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 outline=1 +common lineHeight=23 base=18 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 +page id=0 file="notoCJK_0.png" +chars count=576 +char id=32 x=507 y=185 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=4 page=0 chnl=15 +char id=33 x=449 y=285 width=5 height=14 xoffset=0 yoffset=5 xadvance=5 page=0 chnl=15 +char id=34 x=393 y=315 width=8 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=35 x=285 y=287 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=36 x=500 y=61 width=10 height=18 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=37 x=52 y=257 width=15 height=15 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=38 x=495 y=268 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=39 x=413 y=315 width=5 height=8 xoffset=-1 yoffset=4 xadvance=3 page=0 chnl=15 +char id=40 x=339 y=21 width=7 height=19 xoffset=0 yoffset=4 xadvance=6 page=0 chnl=15 +char id=41 x=330 y=21 width=7 height=19 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=42 x=321 y=317 width=10 height=9 xoffset=-1 yoffset=4 xadvance=8 page=0 chnl=15 +char id=43 x=26 y=322 width=11 height=11 xoffset=-1 yoffset=7 xadvance=9 page=0 chnl=15 +char id=44 x=353 y=316 width=6 height=9 xoffset=-1 yoffset=15 xadvance=5 page=0 chnl=15 +char id=45 x=136 y=334 width=7 height=4 xoffset=-1 yoffset=12 xadvance=5 page=0 chnl=15 +char id=46 x=109 y=334 width=5 height=5 xoffset=0 yoffset=14 xadvance=5 page=0 chnl=15 +char id=47 x=500 y=81 width=8 height=18 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=48 x=406 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=49 x=418 y=286 width=9 height=14 xoffset=0 yoffset=5 xadvance=9 page=0 chnl=15 +char id=50 x=394 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=51 x=382 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=52 x=272 y=287 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=53 x=370 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=54 x=358 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=55 x=346 y=286 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=56 x=334 y=287 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=57 x=322 y=287 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=58 x=502 y=300 width=5 height=11 xoffset=0 yoffset=8 xadvance=5 page=0 chnl=15 +char id=59 x=190 y=238 width=6 height=16 xoffset=-1 yoffset=8 xadvance=5 page=0 chnl=15 +char id=60 x=39 y=322 width=11 height=11 xoffset=-1 yoffset=7 xadvance=9 page=0 chnl=15 +char id=61 x=440 y=315 width=11 height=7 xoffset=-1 yoffset=9 xadvance=9 page=0 chnl=15 +char id=62 x=78 y=321 width=11 height=11 xoffset=-1 yoffset=7 xadvance=9 page=0 chnl=15 +char id=63 x=460 y=252 width=7 height=15 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 +char id=64 x=72 y=221 width=16 height=16 xoffset=-1 yoffset=6 xadvance=14 page=0 chnl=15 +char id=65 x=447 y=269 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=66 x=175 y=289 width=12 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=67 x=75 y=290 width=13 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=68 x=415 y=270 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=69 x=119 y=290 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=70 x=105 y=290 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=71 x=431 y=269 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=72 x=302 y=271 width=15 height=14 xoffset=-1 yoffset=5 xadvance=14 page=0 chnl=15 +char id=73 x=439 y=285 width=8 height=14 xoffset=-1 yoffset=5 xadvance=6 page=0 chnl=15 +char id=74 x=268 y=200 width=9 height=17 xoffset=-2 yoffset=5 xadvance=6 page=0 chnl=15 +char id=75 x=367 y=270 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=76 x=203 y=289 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=77 x=158 y=273 width=17 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=78 x=285 y=271 width=15 height=14 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=79 x=479 y=269 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=80 x=189 y=289 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=81 x=209 y=142 width=14 height=18 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=82 x=30 y=291 width=13 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=83 x=259 y=287 width=11 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=84 x=45 y=291 width=13 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=85 x=335 y=270 width=14 height=14 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=86 x=399 y=270 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=87 x=485 y=252 width=19 height=14 xoffset=-1 yoffset=5 xadvance=17 page=0 chnl=15 +char id=88 x=0 y=291 width=13 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=89 x=15 y=291 width=13 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=90 x=217 y=287 width=12 height=14 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 +char id=91 x=8 y=164 width=6 height=18 xoffset=0 yoffset=4 xadvance=5 page=0 chnl=15 +char id=92 x=497 y=141 width=8 height=18 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=93 x=0 y=164 width=6 height=18 xoffset=-1 yoffset=4 xadvance=5 page=0 chnl=15 +char id=94 x=465 y=313 width=9 height=7 xoffset=0 yoffset=8 xadvance=9 page=0 chnl=15 +char id=95 x=145 y=334 width=11 height=3 xoffset=-1 yoffset=17 xadvance=9 page=0 chnl=15 +char id=96 x=74 y=335 width=6 height=6 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 +char id=97 x=13 y=322 width=11 height=11 xoffset=-1 yoffset=8 xadvance=9 page=0 chnl=15 +char id=98 x=398 y=218 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=99 x=129 y=321 width=10 height=11 xoffset=-1 yoffset=8 xadvance=8 page=0 chnl=15 +char id=100 x=426 y=217 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=101 x=117 y=321 width=10 height=11 xoffset=-1 yoffset=8 xadvance=9 page=0 chnl=15 +char id=102 x=140 y=238 width=10 height=16 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=103 x=52 y=239 width=11 height=16 xoffset=-1 yoffset=8 xadvance=9 page=0 chnl=15 +char id=104 x=440 y=217 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=105 x=469 y=252 width=7 height=15 xoffset=-1 yoffset=4 xadvance=5 page=0 chnl=15 +char id=106 x=95 y=0 width=8 height=20 xoffset=-3 yoffset=4 xadvance=5 page=0 chnl=15 +char id=107 x=412 y=218 width=12 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=108 x=451 y=252 width=7 height=15 xoffset=-1 yoffset=4 xadvance=5 page=0 chnl=15 +char id=109 x=386 y=302 width=18 height=11 xoffset=-1 yoffset=8 xadvance=16 page=0 chnl=15 +char id=110 x=488 y=300 width=12 height=11 xoffset=-1 yoffset=8 xadvance=11 page=0 chnl=15 +char id=111 x=91 y=321 width=11 height=11 xoffset=-1 yoffset=8 xadvance=10 page=0 chnl=15 +char id=112 x=300 y=218 width=12 height=16 xoffset=-1 yoffset=8 xadvance=10 page=0 chnl=15 +char id=113 x=286 y=219 width=12 height=16 xoffset=-1 yoffset=8 xadvance=10 page=0 chnl=15 +char id=114 x=141 y=321 width=10 height=11 xoffset=-1 yoffset=8 xadvance=7 page=0 chnl=15 +char id=115 x=165 y=320 width=9 height=11 xoffset=-1 yoffset=8 xadvance=8 page=0 chnl=15 +char id=116 x=429 y=286 width=8 height=14 xoffset=-1 yoffset=5 xadvance=6 page=0 chnl=15 +char id=117 x=474 y=300 width=12 height=11 xoffset=-1 yoffset=8 xadvance=10 page=0 chnl=15 +char id=118 x=0 y=322 width=11 height=11 xoffset=-1 yoffset=8 xadvance=9 page=0 chnl=15 +char id=119 x=424 y=302 width=16 height=11 xoffset=-1 yoffset=8 xadvance=13 page=0 chnl=15 +char id=120 x=104 y=321 width=11 height=11 xoffset=-1 yoffset=8 xadvance=9 page=0 chnl=15 +char id=121 x=342 y=218 width=12 height=16 xoffset=-2 yoffset=8 xadvance=9 page=0 chnl=15 +char id=122 x=153 y=320 width=10 height=11 xoffset=-1 yoffset=8 xadvance=8 page=0 chnl=15 +char id=123 x=467 y=141 width=8 height=18 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=124 x=111 y=0 width=4 height=20 xoffset=0 yoffset=4 xadvance=4 page=0 chnl=15 +char id=125 x=457 y=141 width=8 height=18 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=126 x=27 y=335 width=11 height=6 xoffset=-1 yoffset=9 xadvance=9 page=0 chnl=15 +char id=160 x=507 y=180 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=4 page=0 chnl=15 +char id=161 x=478 y=252 width=5 height=15 xoffset=0 yoffset=8 xadvance=5 page=0 chnl=15 +char id=162 x=430 y=252 width=10 height=15 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=163 x=310 y=287 width=10 height=14 xoffset=-1 yoffset=5 xadvance=9 page=0 chnl=15 +char id=164 x=52 y=322 width=11 height=11 xoffset=-1 yoffset=7 xadvance=9 page=0 chnl=15 +char id=165 x=201 y=305 width=12 height=13 xoffset=-2 yoffset=6 xadvance=9 page=0 chnl=15 +char id=166 x=105 y=0 width=4 height=20 xoffset=0 yoffset=4 xadvance=4 page=0 chnl=15 +char id=167 x=244 y=200 width=10 height=17 xoffset=3 yoffset=4 xadvance=16 page=0 chnl=15 +char id=168 x=116 y=334 width=9 height=4 xoffset=-1 yoffset=5 xadvance=7 page=0 chnl=15 +char id=169 x=251 y=271 width=15 height=14 xoffset=-1 yoffset=5 xadvance=13 page=0 chnl=15 +char id=170 x=383 y=316 width=8 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=171 x=248 y=317 width=10 height=10 xoffset=-1 yoffset=9 xadvance=8 page=0 chnl=15 +char id=172 x=427 y=315 width=11 height=7 xoffset=-1 yoffset=10 xadvance=9 page=0 chnl=15 +char id=173 x=127 y=334 width=7 height=4 xoffset=-1 yoffset=12 xadvance=5 page=0 chnl=15 +char id=174 x=236 y=317 width=10 height=10 xoffset=-1 yoffset=4 xadvance=8 page=0 chnl=15 +char id=175 x=158 y=333 width=7 height=3 xoffset=0 yoffset=6 xadvance=7 page=0 chnl=15 +char id=176 x=486 y=313 width=7 height=7 xoffset=-1 yoffset=5 xadvance=6 page=0 chnl=15 +char id=177 x=154 y=256 width=15 height=15 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=178 x=270 y=317 width=8 height=10 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=179 x=260 y=317 width=8 height=10 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=180 x=58 y=335 width=6 height=6 xoffset=1 yoffset=4 xadvance=7 page=0 chnl=15 +char id=181 x=26 y=240 width=11 height=16 xoffset=0 yoffset=7 xadvance=10 page=0 chnl=15 +char id=182 x=216 y=200 width=12 height=17 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=183 x=102 y=334 width=5 height=5 xoffset=0 yoffset=10 xadvance=5 page=0 chnl=15 +char id=184 x=504 y=313 width=6 height=7 xoffset=0 yoffset=16 xadvance=7 page=0 chnl=15 +char id=185 x=280 y=317 width=7 height=10 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15 +char id=186 x=403 y=315 width=8 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=187 x=224 y=320 width=10 height=10 xoffset=-1 yoffset=9 xadvance=8 page=0 chnl=15 +char id=188 x=137 y=256 width=15 height=15 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=189 x=375 y=236 width=16 height=15 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 +char id=190 x=120 y=256 width=15 height=15 xoffset=0 yoffset=5 xadvance=15 page=0 chnl=15 +char id=191 x=442 y=252 width=7 height=15 xoffset=0 yoffset=8 xadvance=7 page=0 chnl=15 +char id=192 x=273 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=193 x=353 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=194 x=48 y=202 width=14 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=195 x=337 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=196 x=0 y=203 width=14 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=197 x=225 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=198 x=20 y=275 width=18 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=199 x=400 y=141 width=13 height=18 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=200 x=443 y=141 width=12 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=201 x=498 y=101 width=12 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=202 x=429 y=141 width=12 height=18 xoffset=-1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=203 x=202 y=200 width=12 height=17 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 +char id=204 x=502 y=121 width=8 height=18 xoffset=-1 yoffset=1 xadvance=6 page=0 chnl=15 +char id=205 x=477 y=141 width=8 height=18 xoffset=-1 yoffset=1 xadvance=6 page=0 chnl=15 +char id=206 x=487 y=141 width=8 height=18 xoffset=-1 yoffset=1 xadvance=6 page=0 chnl=15 +char id=207 x=279 y=200 width=8 height=17 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=208 x=319 y=271 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=15 +char id=209 x=142 y=143 width=15 height=18 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=210 x=305 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=211 x=369 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=212 x=491 y=180 width=14 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=213 x=475 y=180 width=14 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=214 x=96 y=201 width=14 height=17 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=215 x=173 y=305 width=12 height=13 xoffset=2 yoffset=6 xadvance=16 page=0 chnl=15 +char id=216 x=209 y=219 width=14 height=16 xoffset=-1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=217 x=241 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=218 x=257 y=142 width=14 height=18 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=219 x=64 y=202 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=220 x=16 y=203 width=14 height=17 xoffset=-1 yoffset=2 xadvance=13 page=0 chnl=15 +char id=221 x=385 y=141 width=13 height=18 xoffset=-1 yoffset=1 xadvance=11 page=0 chnl=15 +char id=222 x=231 y=287 width=12 height=14 xoffset=-1 yoffset=5 xadvance=11 page=0 chnl=15 +char id=223 x=299 y=254 width=12 height=15 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=224 x=13 y=240 width=11 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=225 x=0 y=240 width=11 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=226 x=493 y=217 width=11 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=227 x=367 y=253 width=11 height=15 xoffset=-1 yoffset=4 xadvance=9 page=0 chnl=15 +char id=228 x=341 y=253 width=11 height=15 xoffset=-1 yoffset=4 xadvance=9 page=0 chnl=15 +char id=229 x=480 y=217 width=11 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=230 x=442 y=301 width=15 height=11 xoffset=-1 yoffset=8 xadvance=14 page=0 chnl=15 +char id=231 x=406 y=253 width=10 height=15 xoffset=-1 yoffset=8 xadvance=8 page=0 chnl=15 +char id=232 x=116 y=238 width=10 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=233 x=128 y=238 width=10 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=234 x=104 y=239 width=10 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=235 x=418 y=253 width=10 height=15 xoffset=-1 yoffset=4 xadvance=9 page=0 chnl=15 +char id=236 x=172 y=238 width=7 height=16 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 +char id=237 x=181 y=238 width=7 height=16 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 +char id=238 x=163 y=238 width=7 height=16 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 +char id=239 x=501 y=235 width=9 height=15 xoffset=-2 yoffset=4 xadvance=5 page=0 chnl=15 +char id=240 x=454 y=217 width=11 height=16 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=241 x=327 y=253 width=12 height=15 xoffset=-1 yoffset=4 xadvance=11 page=0 chnl=15 +char id=242 x=91 y=239 width=11 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=243 x=78 y=239 width=11 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=244 x=65 y=239 width=11 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=245 x=393 y=253 width=11 height=15 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=246 x=380 y=253 width=11 height=15 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=247 x=233 y=271 width=16 height=14 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=248 x=215 y=305 width=11 height=13 xoffset=-1 yoffset=7 xadvance=10 page=0 chnl=15 +char id=249 x=384 y=218 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=250 x=356 y=218 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=251 x=314 y=218 width=12 height=16 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=252 x=313 y=254 width=12 height=15 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=253 x=0 y=0 width=12 height=21 xoffset=-2 yoffset=3 xadvance=9 page=0 chnl=15 +char id=254 x=14 y=0 width=11 height=21 xoffset=-1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=255 x=81 y=0 width=12 height=20 xoffset=-2 yoffset=4 xadvance=9 page=0 chnl=15 +char id=8220 x=361 y=316 width=9 height=8 xoffset=-1 yoffset=4 xadvance=7 page=0 chnl=15 +char id=8222 x=372 y=316 width=9 height=8 xoffset=-1 yoffset=14 xadvance=7 page=0 chnl=15 +char id=9829 x=18 y=222 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12288 x=507 y=141 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=16 page=0 chnl=15 +char id=12290 x=476 y=313 width=8 height=7 xoffset=-1 yoffset=14 xadvance=16 page=0 chnl=15 +char id=12353 x=147 y=289 width=12 height=14 xoffset=2 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12354 x=73 y=183 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12355 x=459 y=300 width=13 height=11 xoffset=1 yoffset=9 xadvance=16 page=0 chnl=15 +char id=12356 x=483 y=235 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12357 x=298 y=287 width=10 height=14 xoffset=3 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12358 x=415 y=141 width=12 height=18 xoffset=2 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12359 x=187 y=305 width=12 height=13 xoffset=2 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12360 x=409 y=180 width=15 height=17 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12361 x=53 y=307 width=14 height=13 xoffset=1 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12362 x=108 y=220 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12363 x=271 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12364 x=0 y=184 width=17 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12365 x=32 y=202 width=14 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12366 x=72 y=143 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12367 x=152 y=238 width=9 height=16 xoffset=3 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12368 x=256 y=219 width=13 height=16 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12369 x=375 y=181 width=15 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12370 x=340 y=122 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12371 x=284 y=254 width=13 height=15 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12372 x=426 y=180 width=15 height=17 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12373 x=142 y=201 width=13 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12374 x=57 y=123 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12375 x=443 y=180 width=14 height=17 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12376 x=112 y=201 width=13 height=17 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12377 x=235 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12378 x=209 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12379 x=309 y=200 width=18 height=16 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12380 x=137 y=163 width=18 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12381 x=358 y=181 width=15 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12382 x=396 y=161 width=17 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12383 x=217 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12384 x=277 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12385 x=324 y=181 width=15 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12386 x=430 y=121 width=16 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12387 x=195 y=320 width=13 height=10 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=15 +char id=12388 x=267 y=303 width=17 height=12 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12389 x=218 y=237 width=18 height=15 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12390 x=411 y=236 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12391 x=338 y=236 width=17 height=15 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12392 x=370 y=218 width=12 height=16 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12393 x=392 y=181 width=15 height=17 xoffset=2 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12394 x=477 y=199 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12395 x=160 y=220 width=15 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12396 x=447 y=235 width=16 height=15 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12397 x=491 y=161 width=17 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12398 x=465 y=235 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12399 x=36 y=221 width=16 height=16 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12400 x=479 y=101 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12401 x=247 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12402 x=495 y=199 width=15 height=16 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12403 x=36 y=144 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12404 x=253 y=181 width=16 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12405 x=348 y=200 width=17 height=16 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12406 x=289 y=200 width=18 height=16 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12407 x=386 y=200 width=17 height=16 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12408 x=228 y=303 width=18 height=12 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12409 x=139 y=273 width=17 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12410 x=456 y=285 width=17 height=13 xoffset=-1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12411 x=55 y=183 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12412 x=171 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12413 x=95 y=123 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12414 x=193 y=219 width=14 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12415 x=405 y=200 width=16 height=16 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12416 x=367 y=200 width=17 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12417 x=90 y=221 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12418 x=328 y=218 width=12 height=16 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12419 x=383 y=270 width=14 height=14 xoffset=1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12420 x=472 y=161 width=17 height=17 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12421 x=159 y=305 width=12 height=13 xoffset=2 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12422 x=109 y=182 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12423 x=348 y=302 width=11 height=12 xoffset=3 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12424 x=241 y=219 width=13 height=16 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12425 x=127 y=201 width=13 height=17 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12426 x=500 y=41 width=10 height=18 xoffset=3 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12427 x=188 y=256 width=14 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12428 x=177 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12429 x=177 y=220 width=14 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12430 x=351 y=270 width=14 height=14 xoffset=1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12431 x=163 y=182 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12432 x=393 y=236 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12433 x=181 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12434 x=143 y=220 width=15 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12435 x=54 y=221 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12436 x=289 y=142 width=14 height=18 xoffset=2 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12437 x=463 y=269 width=14 height=14 xoffset=1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12438 x=101 y=306 width=13 height=13 xoffset=2 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12441 x=40 y=335 width=7 height=6 xoffset=-6 yoffset=3 xadvance=0 page=0 chnl=15 +char id=12442 x=49 y=335 width=7 height=6 xoffset=-5 yoffset=3 xadvance=0 page=0 chnl=15 +char id=12443 x=495 y=313 width=7 height=7 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12444 x=0 y=335 width=7 height=7 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12445 x=333 y=317 width=8 height=9 xoffset=4 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12446 x=65 y=322 width=11 height=11 xoffset=4 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12447 x=193 y=142 width=14 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12448 x=453 y=314 width=10 height=7 xoffset=3 yoffset=9 xadvance=16 page=0 chnl=15 +char id=12449 x=334 y=303 width=12 height=12 xoffset=2 yoffset=9 xadvance=16 page=0 chnl=15 +char id=12450 x=126 y=220 width=15 height=16 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12451 x=374 y=302 width=10 height=12 xoffset=2 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12452 x=157 y=201 width=13 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12453 x=161 y=289 width=12 height=14 xoffset=2 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12454 x=459 y=180 width=14 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12455 x=289 y=317 width=14 height=9 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=15 +char id=12456 x=494 y=285 width=16 height=13 xoffset=0 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12457 x=116 y=306 width=13 height=13 xoffset=1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12458 x=459 y=199 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12459 x=307 y=181 width=15 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12460 x=453 y=161 width=17 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12461 x=289 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12462 x=285 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12463 x=80 y=202 width=14 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12464 x=448 y=121 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12465 x=0 y=222 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12466 x=19 y=124 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12467 x=304 y=303 width=14 height=12 xoffset=1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12468 x=429 y=235 width=16 height=15 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12469 x=199 y=181 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12470 x=160 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12471 x=103 y=257 width=15 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12472 x=341 y=181 width=15 height=17 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12473 x=69 y=257 width=15 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12474 x=145 y=182 width=16 height=17 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12475 x=86 y=257 width=15 height=15 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12476 x=278 y=237 width=18 height=15 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12477 x=204 y=254 width=14 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12478 x=322 y=122 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12479 x=321 y=142 width=14 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12480 x=38 y=124 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12481 x=127 y=182 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12482 x=434 y=161 width=17 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12483 x=320 y=303 width=12 height=12 xoffset=2 yoffset=9 xadvance=16 page=0 chnl=15 +char id=12484 x=252 y=254 width=14 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12485 x=266 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12486 x=441 y=199 width=16 height=16 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12487 x=228 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12488 x=256 y=200 width=10 height=17 xoffset=4 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12489 x=230 y=200 width=12 height=17 xoffset=3 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12490 x=91 y=182 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12491 x=406 y=302 width=16 height=11 xoffset=0 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12492 x=35 y=258 width=15 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12493 x=484 y=121 width=16 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12494 x=90 y=290 width=13 height=14 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12495 x=176 y=320 width=17 height=10 xoffset=-1 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12496 x=196 y=273 width=17 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12497 x=177 y=273 width=17 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12498 x=60 y=290 width=13 height=14 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12499 x=171 y=256 width=15 height=15 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12500 x=0 y=258 width=16 height=15 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12501 x=236 y=254 width=14 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12502 x=18 y=144 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12503 x=90 y=143 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12504 x=248 y=303 width=17 height=12 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12505 x=120 y=273 width=17 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12506 x=475 y=285 width=17 height=13 xoffset=-1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12507 x=357 y=236 width=16 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12508 x=415 y=161 width=17 height=17 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12509 x=37 y=183 width=16 height=17 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12510 x=286 y=303 width=16 height=12 xoffset=0 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12511 x=354 y=253 width=11 height=15 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12512 x=18 y=258 width=15 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12513 x=268 y=254 width=14 height=15 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12514 x=215 y=271 width=16 height=14 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12515 x=131 y=306 width=12 height=13 xoffset=2 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12516 x=423 y=199 width=16 height=16 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12517 x=305 y=317 width=14 height=9 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=15 +char id=12518 x=0 y=307 width=16 height=13 xoffset=0 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12519 x=210 y=320 width=12 height=10 xoffset=2 yoffset=9 xadvance=16 page=0 chnl=15 +char id=12520 x=85 y=306 width=14 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12521 x=225 y=219 width=14 height=16 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12522 x=39 y=239 width=11 height=16 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12523 x=0 y=275 width=18 height=14 xoffset=-1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12524 x=36 y=307 width=15 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12525 x=69 y=306 width=14 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12526 x=145 y=305 width=12 height=13 xoffset=2 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12527 x=220 y=254 width=14 height=15 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12528 x=19 y=183 width=16 height=17 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 +char id=12529 x=18 y=307 width=16 height=13 xoffset=0 yoffset=6 xadvance=16 page=0 chnl=15 +char id=12530 x=271 y=219 width=13 height=16 xoffset=2 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12531 x=268 y=271 width=15 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12532 x=304 y=122 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12533 x=133 y=289 width=12 height=14 xoffset=2 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12534 x=245 y=287 width=12 height=14 xoffset=2 yoffset=7 xadvance=16 page=0 chnl=15 +char id=12535 x=190 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12536 x=140 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12537 x=329 y=200 width=17 height=16 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12538 x=54 y=144 width=16 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=12539 x=66 y=335 width=6 height=6 xoffset=5 yoffset=10 xadvance=16 page=0 chnl=15 +char id=12540 x=9 y=335 width=16 height=6 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 +char id=12541 x=343 y=317 width=8 height=9 xoffset=4 yoffset=8 xadvance=16 page=0 chnl=15 +char id=12542 x=361 y=302 width=11 height=12 xoffset=4 yoffset=5 xadvance=16 page=0 chnl=15 +char id=12543 x=172 y=201 width=13 height=17 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=19968 x=82 y=334 width=18 height=5 xoffset=-1 yoffset=8 xadvance=16 page=0 chnl=15 +char id=19975 x=217 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=19979 x=120 y=103 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=19981 x=100 y=103 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20037 x=80 y=103 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20096 x=60 y=103 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20102 x=394 y=121 width=16 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20107 x=40 y=103 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20108 x=60 y=274 width=18 height=14 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 +char id=20154 x=20 y=104 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20260 x=0 y=104 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20307 x=480 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20316 x=320 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20320 x=340 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20663 x=320 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20814 x=120 y=63 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20837 x=80 y=63 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20853 x=60 y=63 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20891 x=40 y=63 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20986 x=0 y=144 width=16 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=20999 x=480 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21040 x=460 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21147 x=133 y=123 width=17 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21151 x=440 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21315 x=420 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21507 x=377 y=162 width=17 height=17 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21527 x=152 y=122 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21534 x=400 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=21705 x=380 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=22622 x=360 y=41 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=22825 x=340 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=22833 x=260 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=22836 x=220 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=22909 x=160 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=23376 x=140 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=23398 x=120 y=43 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=23544 x=80 y=43 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=23558 x=60 y=43 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24120 x=40 y=43 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24180 x=117 y=0 width=18 height=19 xoffset=-1 yoffset=2 xadvance=16 page=0 chnl=15 +char id=24320 x=20 y=44 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24433 x=0 y=44 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24471 x=469 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24515 x=16 y=164 width=19 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24605 x=197 y=162 width=18 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=24651 x=357 y=162 width=18 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=25105 x=449 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26080 x=409 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26082 x=369 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26085 x=187 y=200 width=13 height=17 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 +char id=26131 x=457 y=0 width=17 height=19 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26178 x=0 y=124 width=17 height=18 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26368 x=440 y=101 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26377 x=420 y=101 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26408 x=400 y=101 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=26543 x=380 y=101 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=27493 x=360 y=101 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=27515 x=340 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=27714 x=320 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=27835 x=300 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=27963 x=280 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=28937 x=260 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=29483 x=240 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=29503 x=220 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=29627 x=200 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=29702 x=157 y=163 width=18 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=29827 x=180 y=102 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=30002 x=159 y=142 width=15 height=18 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=30045 x=460 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=30693 x=440 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=31119 x=420 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=31169 x=348 y=21 width=19 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=31348 x=400 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32299 x=380 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32368 x=360 y=81 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32705 x=340 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32769 x=257 y=162 width=18 height=17 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32773 x=300 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32780 x=280 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32988 x=260 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=32993 x=240 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=33021 x=220 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=33853 x=200 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=34382 x=180 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=34678 x=160 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=34892 x=140 y=82 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=35265 x=120 y=83 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=35328 x=100 y=83 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36208 x=80 y=83 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36335 x=60 y=83 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36523 x=40 y=83 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36817 x=20 y=84 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36820 x=0 y=84 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36861 x=480 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36941 x=460 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=36965 x=440 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=37326 x=420 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=38590 x=400 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=38750 x=380 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39034 x=360 y=61 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39118 x=300 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39135 x=280 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39340 x=260 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39532 x=240 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=39543 x=220 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=40575 x=200 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=40614 x=180 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=15 +char id=44040 x=160 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44049 x=357 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44057 x=140 y=62 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44060 x=19 y=23 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44163 x=145 y=21 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44221 x=412 y=121 width=16 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44256 x=238 y=237 width=18 height=15 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=44397 x=237 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44536 x=80 y=274 width=18 height=14 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 +char id=44544 x=100 y=63 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=44592 x=282 y=21 width=14 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45033 x=466 y=121 width=16 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45149 x=237 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=45208 x=437 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45209 x=0 y=23 width=17 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45212 x=20 y=64 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45216 x=0 y=64 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45320 x=198 y=21 width=15 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45328 x=176 y=142 width=15 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45397 x=495 y=0 width=15 height=19 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=45576 x=297 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=45716 x=317 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=45720 x=337 y=162 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=45768 x=266 y=21 width=14 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=45796 x=27 y=0 width=18 height=20 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=46020 x=40 y=275 width=18 height=14 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 +char id=46024 x=117 y=163 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=46160 x=320 y=42 width=18 height=18 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=46301 x=300 y=42 width=18 height=18 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=46384 x=257 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=46972 x=197 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=46976 x=280 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47000 x=163 y=21 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47196 x=258 y=237 width=18 height=15 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=47336 x=240 y=42 width=18 height=18 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=47476 x=298 y=237 width=18 height=15 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=47484 x=97 y=163 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=47532 x=65 y=0 width=14 height=20 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 +char id=47560 x=277 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47564 x=200 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47568 x=180 y=42 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47588 x=127 y=21 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47673 x=181 y=21 width=15 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=47784 x=318 y=236 width=18 height=15 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=48148 x=297 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=48152 x=317 y=0 width=18 height=19 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=48373 x=217 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49373 x=376 y=121 width=16 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49436 x=73 y=22 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49464 x=55 y=22 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49688 x=377 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49845 x=397 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49884 x=125 y=143 width=15 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=49892 x=358 y=122 width=16 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50420 x=100 y=43 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50500 x=417 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50504 x=114 y=123 width=17 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50506 x=460 y=101 width=17 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50508 x=76 y=123 width=17 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50612 x=215 y=21 width=15 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50631 x=249 y=21 width=15 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50640 x=47 y=0 width=16 height=20 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=50728 x=77 y=163 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=50836 x=198 y=237 width=18 height=15 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 +char id=50857 x=57 y=164 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=50948 x=37 y=22 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=50976 x=489 y=21 width=18 height=18 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=51012 x=37 y=164 width=18 height=17 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=51060 x=298 y=21 width=14 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51080 x=91 y=22 width=16 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51088 x=337 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51228 x=109 y=22 width=16 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51460 x=429 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51648 x=232 y=21 width=15 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=51652 x=108 y=143 width=15 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=52380 x=476 y=0 width=17 height=19 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=52840 x=314 y=21 width=14 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=54032 x=389 y=21 width=18 height=18 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=54532 x=100 y=274 width=18 height=14 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 +char id=54616 x=177 y=0 width=18 height=19 xoffset=-1 yoffset=3 xadvance=15 page=0 chnl=15 +char id=54620 x=157 y=0 width=18 height=19 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=54988 x=137 y=0 width=18 height=19 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=15 +char id=65292 x=420 y=315 width=5 height=8 xoffset=0 yoffset=15 xadvance=16 page=0 chnl=15 +char id=65311 x=467 y=217 width=11 height=16 xoffset=2 yoffset=4 xadvance=16 page=0 chnl=15 diff --git a/examples/text/resources/notoCJK_0.png b/examples/text/resources/notoCJK_0.png new file mode 100644 index 00000000..4f5cd4b8 Binary files /dev/null and b/examples/text/resources/notoCJK_0.png differ diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c new file mode 100644 index 00000000..1538ef6f --- /dev/null +++ b/examples/text/text_unicode.c @@ -0,0 +1,311 @@ +/******************************************************************************************* +* +* raylib [text] example - Using unicode with raylib +* +* 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) +* +* Copyright (c) 2019 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include +#include +#include "raylib.h" + +#define SIZEOF(A) (sizeof(A)/sizeof(A[0])) +#define EMOJI_PER_WIDTH 8 +#define EMOJI_PER_HEIGHT 4 + +// String containing 180 emoji codepoints separated by a '\0' char +const char* const emojiCodepoints = "\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00" + "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00" + "\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00" + "\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00" + "\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00" + "\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00" + "\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00" + "\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00" + "\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00" + "\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00" + "\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00" + "\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00" + "\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00" + "\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00" + "\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00" + "\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00" + "\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00" + "\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00" + "\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00" + "\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00" + "\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00" + "\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00" + "\xF0\x9F\x92\x9F\x00\xF0\x9F\x92\x8C\x00\xF0\x9F\x92\xA4\x00\xF0\x9F\x92\xA2\x00\xF0\x9F\x92\xA3\x00"; + +struct { + char* text; + char* language; +} const messages[] = { // Array containing all of the emojis messages + {"\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C" + "\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"}, + {"\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74" + "\x74\x65\x72\x74\x2E", "German"}, + {"\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F" + "\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"}, + {"\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB" + "\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"}, + {"\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5" + "\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF" + "\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"}, + {"\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5" + "\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"}, + {"\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87" + "\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"}, + {"\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F" + "\x77\x61\x6E\x65\x2E", "Polish"}, + {"\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98" + "\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"}, + {"\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20" + "\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"}, + {"\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"}, + {"\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8" + "\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20" + "\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1" + "\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1" + "\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2" + "\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC" + "\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"}, + {"\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70" + "\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"}, + {"\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20" + "\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78" + "\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"}, + {"\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20" + "\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE" + "\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"}, + {"\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD" + "\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"}, + {"\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"}, + {"\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"}, + + {"\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"}, + {"\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"}, + {"\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"}, + {"\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"}, + {"\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"}, + {"\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"}, + {"\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"}, + {"\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"}, + {"\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"}, + {"\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"}, + {"\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"}, + {"\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"}, + + {"\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"}, + {"\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"}, + {"\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"}, + {"\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"}, + {"\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"}, + {"\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"}, + {"\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"}, + {"\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"}, + + {"\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20" + "\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E" + "\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98" + "\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"}, + {"\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"}, + {"\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"}, + {"\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4" + "\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"}, + {"\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"}, + {"\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"}, + {"\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"}, + {"\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"}, + {"\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"}, +}; + + +// Forward declaration of our function +//-------------------------------------------------------------------------------------- +void Draw(); // Draws emojis and the text bubbles +static inline void RandomizeEmoji(); // Fills the emoji array with random emojis +//-------------------------------------------------------------------------------------- + + +// Global variables +//-------------------------------------------------------------------------------------- +// Arrays that holds the random emojis +struct { + int index; // Index inside `emojiCodepoints` + int message; // Message index + Color color; // Emoji color +} emoji[EMOJI_PER_WIDTH*EMOJI_PER_HEIGHT] = {0}; + +const int screenWidth = 800, screenHeight = 450; + +// Fonts that we use: `font2` is for asian languages, `font3` is the emoji font and `font1` is used for everything else +Font font1 = {0}, font2 = {0}, font3 = {0}; +Vector2 hoveredPos = {0,0}, selectedPos = {0,0}; +int hovered = -1, selected = -1; +//-------------------------------------------------------------------------------------- + + +int main(int argc, char **argv) +{ + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); + InitWindow(screenWidth, screenHeight, "raylib - unicode test"); + SetTargetFPS(60); + + // Load the font resources + font1 = LoadFont("resources/dejavu.fnt"); + font2 = LoadFont("resources/notoCJK.fnt"); + font3 = LoadFont("resources/emoji.fnt"); + + // Set a random set of emojis when starting up + RandomizeEmoji(); + //-------------------------------------------------------------------------------------- + + // Main loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + // Add a new set of emojis when SPACE is pressed + if(IsKeyPressed(KEY_SPACE)) + { + RandomizeEmoji(); + } + // Set the selected emoji and copy its text to clipboard + if(IsMouseButtonPressed(MOUSE_LEFT_BUTTON) && hovered != -1 && hovered != selected) + { + selected = hovered; + selectedPos = hoveredPos; + SetClipboardText(messages[emoji[selected].message].text); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw emoji and the text bubbles + Draw(); + + // Draw the info text + DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650)/2, screenHeight - 40, 20, GRAY); + DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484)/2, screenHeight - 16, 10, GRAY); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + // Unload all of the fonts + UnloadFont(font1); + UnloadFont(font2); + UnloadFont(font3); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +// Fills the emoji array with random emoji (only those emojis present in font3) +static inline void RandomizeEmoji() +{ + hovered = selected = -1; + int start = GetRandomValue(45, 360); + for(int i=0; i < SIZEOF(emoji); ++i) + { + emoji[i].index = GetRandomValue(0, 179)*5; // 0-179 emoji codepoints (from emoji char array) each 4bytes + null char + // Generate a random color for this emoji + Vector3 hsv = {(start*(i+1))%360, 0.6f, 0.85f}; + emoji[i].color = Fade(ColorFromHSV(hsv), 0.8f); + // Set a random message for this emoji + emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1); + } +} + + +void Draw() +{ + // Draw random emoji in the background + Vector2 pos = {28.8f, 10.0f}; + Vector2 mouse = GetMousePosition(); + hovered = -1; + for(int i=0; ibaseSize, 1.0f); + if(sz.x > 300) { + sz.y *= sz.x/300; + sz.x = 300; + } else if(sz.x < 160) sz.x = 160; + Rectangle msgRect = { selectedPos.x - 38.8f, selectedPos.y, 2 * horizontalPadding + sz.x, 2 * verticalPadding + sz.y}; + msgRect.y -= msgRect.height; + Vector2 a = {selectedPos.x, msgRect.y + msgRect.height}, b = {a.x + 8, a.y + 10}, c= {a.x+10, a.y}; // coordinates for the chat bubble triangle + // Don't go outside the screen + if(msgRect.x < 10) msgRect.x += 28; + if(msgRect.y < 10) + { + msgRect.y = selectedPos.y + 84; + a.y = msgRect.y; + c.y = a.y; + b.y = a.y - 10; + // Swap values so we can actually render the triangle :( + Vector2 tmp = a; + a = b; + b = tmp; + } + if(msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10; + // Draw chat bubble + DrawRectangleRec(msgRect, emoji[selected].color); + DrawTriangle(a, b, c, emoji[selected].color); + + // Draw the main text message + Rectangle textRect = {msgRect.x + horizontalPadding/2, msgRect.y + verticalPadding/2, msgRect.width - horizontalPadding, msgRect.height}; + DrawTextRec(*font, messages[message].text, textRect, font->baseSize, 1.0f, true, WHITE); + + // Draw the info text below the main message + int size = strlen(messages[message].text); + unsigned int len = TextCountCodepoints(messages[message].text); + const char* info = TextFormat("%s %u characters %i bytes", messages[message].language, len, size); + sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); + Vector2 pos = { textRect.x + textRect.width - sz.x, msgRect.y + msgRect.height - sz.y - 2 }; + DrawText(info, pos.x, pos.y, 10, RAYWHITE); + } +} + -- cgit v1.2.3 From 3dda1c41ec74ba7a55926de971289ddaf2f4a46e Mon Sep 17 00:00:00 2001 From: Vlad Adrian Date: Sun, 21 Apr 2019 21:51:03 +0300 Subject: Fixed formatting! Grumble, grumble... --- examples/text/text_unicode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index 1538ef6f..f383dcad 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -270,10 +270,14 @@ void Draw() // Calculate size for the message box (approximate the height and width) Vector2 sz = MeasureTextEx(*font, messages[message].text, font->baseSize, 1.0f); - if(sz.x > 300) { + if(sz.x > 300) + { sz.y *= sz.x/300; sz.x = 300; - } else if(sz.x < 160) sz.x = 160; + } + else if(sz.x < 160) + sz.x = 160; + Rectangle msgRect = { selectedPos.x - 38.8f, selectedPos.y, 2 * horizontalPadding + sz.x, 2 * verticalPadding + sz.y}; msgRect.y -= msgRect.height; Vector2 a = {selectedPos.x, msgRect.y + msgRect.height}, b = {a.x + 8, a.y + 10}, c= {a.x+10, a.y}; // coordinates for the chat bubble triangle -- cgit v1.2.3 From 98070982bf7b61c8595eb855acfebd721f68af68 Mon Sep 17 00:00:00 2001 From: Vlad Adrian Date: Sun, 21 Apr 2019 21:57:57 +0300 Subject: Fix formatting ...not very good at this --- examples/text/text_unicode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index f383dcad..d960305a 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -189,7 +189,7 @@ int main(int argc, char **argv) // Draw //---------------------------------------------------------------------------------- - BeginDrawing(); + BeginDrawing(); ClearBackground(RAYWHITE); // Draw emoji and the text bubbles @@ -271,12 +271,12 @@ void Draw() // Calculate size for the message box (approximate the height and width) Vector2 sz = MeasureTextEx(*font, messages[message].text, font->baseSize, 1.0f); if(sz.x > 300) - { + { sz.y *= sz.x/300; sz.x = 300; } - else if(sz.x < 160) - sz.x = 160; + else if(sz.x < 160) + sz.x = 160; Rectangle msgRect = { selectedPos.x - 38.8f, selectedPos.y, 2 * horizontalPadding + sz.x, 2 * verticalPadding + sz.y}; msgRect.y -= msgRect.height; -- cgit v1.2.3 From 4b8d06f50139b33d7670c286e2e3f006843ac8ba Mon Sep 17 00:00:00 2001 From: Jak <5613046+Syphonx@users.noreply.github.com> Date: Mon, 22 Apr 2019 19:03:00 +0100 Subject: [rnet] module WIP (#809) Added experimental network module --- .gitignore | 1 + examples/network/network_ping_pong.c | 225 ++++ examples/network/network_resolve_host.c | 80 ++ examples/network/network_tcp_client.c | 151 +++ examples/network/network_tcp_server.c | 165 +++ examples/network/network_test.c | 148 +++ examples/network/network_udp_client.c | 128 ++ examples/network/network_udp_server.c | 134 ++ src/raylib.h | 176 +++ src/rnet.c | 2023 +++++++++++++++++++++++++++++++ src/rnet.h | 228 ++++ 11 files changed, 3459 insertions(+) create mode 100644 examples/network/network_ping_pong.c create mode 100644 examples/network/network_resolve_host.c create mode 100644 examples/network/network_tcp_client.c create mode 100644 examples/network/network_tcp_server.c create mode 100644 examples/network/network_test.c create mode 100644 examples/network/network_udp_client.c create mode 100644 examples/network/network_udp_server.c create mode 100644 src/rnet.c create mode 100644 src/rnet.h (limited to 'examples') diff --git a/.gitignore b/.gitignore index 963342a0..08e661af 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ templates/android_project/output GPATH GRTAGS GTAGS +.vs diff --git a/examples/network/network_ping_pong.c b/examples/network/network_ping_pong.c new file mode 100644 index 00000000..719f6739 --- /dev/null +++ b/examples/network/network_ping_pong.c @@ -0,0 +1,225 @@ +/******************************************************************************************* + * + * raylib [network] example - Client/Server ping-pong + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +bool client_connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true}; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true}; +SocketConfig connection_cfg = {.nonblocking = true}; +SocketResult *server_res = NULL; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +Socket * connection = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + // If the server is configured as UDP, ignore connection requests + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + ping = true; + connected = true; + } else { + // If the client is connected, run the server code to check for a connection + if (client_connected) { + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } + } else { + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { + client_connected = true; + } + elapsed = 0.0f; + } + } + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + if (IsSocketReady(server_res->socket)) { + bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + } + } else { + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pingmsg, msglen); + } else { + SocketSend(client_res->socket, pingmsg, msglen); + } + } else if (pong) { + pong = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pongmsg, msglen); + } else { + SocketSend(client_res->socket, pongmsg, msglen); + } + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(3); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_resolve_host.c b/examples/network/network_resolve_host.c new file mode 100644 index 00000000..195e03b5 --- /dev/null +++ b/examples/network/network_resolve_host.c @@ -0,0 +1,80 @@ +/******************************************************************************************* + * + * raylib [network] example - Resolve Host + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +char buffer[ADDRESS_IPV6_ADDRSTRLEN]; +uint16_t port = 0; + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); + + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + AddressInformation* addr = AllocAddressList(1); + int count = ResolveHost( + NULL, + "5210", + ADDRESS_TYPE_IPV4, + 0 // Uncomment any of these flags + // ADDRESS_INFO_NUMERICHOST // or try them in conjunction to + // ADDRESS_INFO_NUMERICSERV // specify custom behaviour from + // ADDRESS_INFO_DNS_ONLY // the function getaddrinfo() + // ADDRESS_INFO_ALL // + // ADDRESS_INFO_FQDN // e.g. ADDRESS_INFO_CANONNAME | ADDRESS_INFO_NUMERICSERV + , + addr + ); + + if (count > 0) + { + GetAddressHostAndPort(addr[0], buffer, &port); + TraceLog(LOG_INFO, "Resolved to ip %s::%d\n", buffer, port); + } + + // Main game loop + while (!WindowShouldClose()) + { + // Draw + BeginDrawing(); + + // Clear + ClearBackground(RAYWHITE); + + // End draw + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_tcp_client.c b/examples/network/network_tcp_client.c new file mode 100644 index 00000000..6eed205a --- /dev/null +++ b/examples/network/network_tcp_client.c @@ -0,0 +1,151 @@ +/******************************************************************************************* + * + * raylib [network] example - TCP Client + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true}; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { connected = true; } + elapsed = 0.0f; + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_tcp_server.c b/examples/network/network_tcp_server.c new file mode 100644 index 00000000..89e9c181 --- /dev/null +++ b/examples/network/network_tcp_server.c @@ -0,0 +1,165 @@ +/******************************************************************************************* + * + * raylib [network] example - TCP Server + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true}; +SocketConfig connection_cfg = {.nonblocking = true}; +SocketResult *server_res = NULL; +SocketSet * socket_set = NULL; +Socket * connection = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(connection, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(connection, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(2); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_test.c b/examples/network/network_test.c new file mode 100644 index 00000000..f18a8b13 --- /dev/null +++ b/examples/network/network_test.c @@ -0,0 +1,148 @@ +/******************************************************************************************* + * + * raylib [network] example - Network Test + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include + +void test_network_initialise() +{ + assert(InitNetwork() == true); +} + +void test_socket_result() +{ + SocketResult *result = AllocSocketResult(); + assert(result != NULL); + FreeSocketResult(&result); + assert(result == NULL); +} + +void test_socket() +{ + Socket *socket = AllocSocket(); + assert(socket != NULL); + FreeSocket(&socket); + assert(socket == NULL); +} + +void test_resolve_ip() +{ + const char *host = "8.8.8.8"; + const char *port = "8080"; + char ip[ADDRESS_IPV6_ADDRSTRLEN]; + char service[ADDRESS_MAXSERV]; + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DEFAULT, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NOFQDN, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NAMEREQD, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICSERV, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DGRAM, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); +} + +void test_resolve_host() +{ + const char * address = "localhost"; + const char * port = "80"; + AddressInformation *addr = AllocAddressList(3); + int count = ResolveHost(address, port, ADDRESS_TYPE_ANY, 0, addr); + + assert(GetAddressFamily(addr[0]) == ADDRESS_TYPE_IPV6); + assert(GetAddressFamily(addr[1]) == ADDRESS_TYPE_IPV4); + assert(GetAddressSocketType(addr[0]) == 0); + assert(GetAddressProtocol(addr[0]) == 0); + // for (size_t i = 0; i < count; i++) { PrintAddressInfo(addr[i]); } +} + +void test_address() +{ +} + +void test_address_list() +{ +} + +void test_socket_create() +{ + SocketConfig server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true}; + Socket * socket = AllocSocket(); + SocketResult *server_res = AllocSocketResult(); + SocketSet * socket_set = AllocSocketSet(1); + assert(SocketCreate(&server_cfg, server_res)); + assert(AddSocket(socket_set, server_res->socket)); + assert(SocketListen(&server_cfg, server_res)); +} + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - network test"); + SetTargetFPS(60); + + // Run the tests + test_network_initialise(); + test_resolve_host(); + // test_socket_create(); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + EndDrawing(); + } + CloseWindow(); + + return 0; +} \ No newline at end of file diff --git a/examples/network/network_udp_client.c b/examples/network/network_udp_client.c new file mode 100644 index 00000000..c1c89c8d --- /dev/null +++ b/examples/network/network_udp_client.c @@ -0,0 +1,128 @@ +/******************************************************************************************* + * + * raylib [network] example - UDP Client + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_UDP, .nonblocking = true}; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + ping = true; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_udp_server.c b/examples/network/network_udp_server.c new file mode 100644 index 00000000..982cdf63 --- /dev/null +++ b/examples/network/network_udp_server.c @@ -0,0 +1,134 @@ +/******************************************************************************************* + * + * raylib [network] example - UDP Server + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * This example has been created using raylib 2.0 (www.raylib.com) + * raylib is licensed under an unmodified zlib/libpng license (View raylib.h + *for details) + * + * Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .server = true, .type = SOCKET_UDP, .nonblocking = true}; +SocketResult *server_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + // int bytesRecv = 0; + // if (IsSocketReady(server_res->socket)) { + // bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + // } + int bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(server_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(server_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 6a5f0ef8..710f69b6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -75,6 +75,7 @@ #define RAYLIB_H #include // Required for: va_list - Only used by TraceLogCallback +#include // Required for rnet #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) @@ -99,6 +100,11 @@ // Shader and material limits #define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct #define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + +// Network connection related defines +#define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options +#define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels +#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. @@ -442,6 +448,100 @@ typedef struct VrDeviceInfo { float lensDistortionValues[4]; // HMD lens distortion constant parameters float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; + +// Network typedefs +typedef uint32_t SocketChannel; +typedef struct _AddressInformation * AddressInformation; +typedef struct _SocketAddress * SocketAddress; +typedef struct _SocketAddressIPv4 * SocketAddressIPv4; +typedef struct _SocketAddressIPv6 * SocketAddressIPv6; +typedef struct _SocketAddressStorage *SocketAddressStorage; + +// IPAddress definition (in network byte order) +typedef struct IPAddress +{ + unsigned long host; /* 32-bit IPv4 host address */ + unsigned short port; /* 16-bit protocol port */ +} IPAddress; + +// An option ID, value, sizeof(value) tuple for setsockopt(2). +typedef struct SocketOpt +{ + int id; + void *value; + int valueLen; +} SocketOpt; + +typedef enum +{ + SOCKET_TCP = 0, // SOCK_STREAM + SOCKET_UDP = 1 // SOCK_DGRAM +} SocketType; + +typedef struct UDPChannel +{ + int numbound; // The total number of addresses this channel is bound to + IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to +} UDPChannel; + +typedef struct Socket +{ + int ready; // Is the socket ready? i.e. has information + int status; // The last status code to have occured using this socket + bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) + SocketChannel channel; // The socket handle id + SocketType type; // Is this socket a TCP or UDP socket? + bool isIPv6; // Is this socket address an ipv6 address? + SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) + SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) + + struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to +} Socket; + +typedef struct SocketSet +{ + int numsockets; + int maxsockets; + struct Socket **sockets; +} SocketSet; + +typedef struct SocketDataPacket +{ + int channel; // The src/dst channel of the packet + unsigned char *data; // The packet data + int len; // The length of the packet data + int maxlen; // The size of the data buffer + int status; // packet status after sending + IPAddress address; // The source/dest address of an incoming/outgoing packet +} SocketDataPacket; + +// Configuration for a socket. +typedef struct SocketConfig +{ + char * host; // The host address in xxx.xxx.xxx.xxx form + char * port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + SocketType type; // The type of socket, TCP/UDP + bool nonblocking; // non-blocking operation? + int backlog_size; // set a custom backlog size + SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; +} SocketConfig; + +// Result from calling open with a given config. +typedef struct SocketResult +{ + int status; + Socket *socket; +} SocketResult; + +// +typedef struct Packet +{ + uint32_t size; // The total size of bytes in data + uint32_t offs; // The offset to data access + uint32_t maxs; // The max size of data + uint8_t *data; // Data stored in network byte order +} Packet; //---------------------------------------------------------------------------------- // Enumerators Definition @@ -1407,6 +1507,82 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) + +//------------------------------------------------------------------------------------ +// Network (Module: network) +//------------------------------------------------------------------------------------ + +// Initialisation and cleanup +RLAPI bool InitNetwork(void); +RLAPI void CloseNetwork(void); + +// Address API +RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +RLAPI int GetAddressFamily(AddressInformation address); +RLAPI int GetAddressSocketType(AddressInformation address); +RLAPI int GetAddressProtocol(AddressInformation address); +RLAPI char* GetAddressCanonName(AddressInformation address); +RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); +RLAPI void PrintAddressInfo(AddressInformation address); + +// Address Memory API +RLAPI AddressInformation AllocAddress(); +RLAPI void FreeAddress(AddressInformation *addressInfo); +RLAPI AddressInformation *AllocAddressList(int size); + +// Socket API +RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result); +RLAPI bool SocketBind(SocketConfig *config, SocketResult *result); +RLAPI bool SocketListen(SocketConfig *config, SocketResult *result); +RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result); +RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config); + +// UDP Socket API +RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +RLAPI void SocketUnsetChannel(Socket *socket, int channel); + +// UDP DataPacket API +RLAPI SocketDataPacket *AllocPacket(int size); +RLAPI int ResizePacket(SocketDataPacket *packet, int newsize); +RLAPI void FreePacket(SocketDataPacket *packet); +RLAPI SocketDataPacket **AllocPacketList(int count, int size); +RLAPI void FreePacketList(SocketDataPacket **packets); + +// General Socket API +RLAPI int SocketSend(Socket *sock, const void *datap, int len); +RLAPI int SocketReceive(Socket *sock, void *data, int maxlen); +RLAPI void SocketClose(Socket *sock); +RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock); +RLAPI char* GetSocketAddressHost(SocketAddressStorage storage); +RLAPI short GetSocketAddressPort(SocketAddressStorage storage); + +// Socket Memory API +RLAPI Socket *AllocSocket(); +RLAPI void FreeSocket(Socket **sock); +RLAPI SocketResult *AllocSocketResult(); +RLAPI void FreeSocketResult(SocketResult **result); +RLAPI SocketSet *AllocSocketSet(int max); +RLAPI void FreeSocketSet(SocketSet *sockset); + +// Socket I/O API +RLAPI bool IsSocketReady(Socket *sock); +RLAPI bool IsSocketConnected(Socket *sock); +RLAPI int AddSocket(SocketSet *set, Socket *sock); +RLAPI int RemoveSocket(SocketSet *set, Socket *sock); +RLAPI int CheckSockets(SocketSet *set, unsigned int timeout); + +// Packet API +void PacketSend(Packet *packet); +void PacketReceive(Packet *packet); +void PacketWrite8(Packet *packet, uint16_t value); +void PacketWrite16(Packet *packet, uint16_t value); +void PacketWrite32(Packet *packet, uint32_t value); +void PacketWrite64(Packet *packet, uint64_t value); +uint16_t PacketRead8(Packet *packet); +uint16_t PacketRead16(Packet *packet); +uint32_t PacketRead32(Packet *packet); +uint64_t PacketRead64(Packet *packet); #if defined(__cplusplus) } diff --git a/src/rnet.c b/src/rnet.c new file mode 100644 index 00000000..0a47b44f --- /dev/null +++ b/src/rnet.c @@ -0,0 +1,2023 @@ +/********************************************************************************************* +* +* rnet - A simple and easy-to-use network module for raylib +* +* FEATURES: +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API +* +* DEPENDENCIES: +* raylib.h - TraceLog +* rnet.h - platform-specific network includes +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019): +* - Initial version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Check if config flags have been externally provided on compilation line +//---------------------------------------------------------------------------------- + +#include "rnet.h" + +#include "raylib.h" + +#include // Required for: assert() +#include // Required for: FILE, fopen(), fclose(), fread() +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strncmp() + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +#define NET_DEBUG_ENABLED (1) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +typedef struct _SocketAddress +{ + struct sockaddr address; +} _SocketAddress; + +typedef struct _SocketAddressIPv4 +{ + struct sockaddr_in address; +} _SocketAddressIPv4; + +typedef struct _SocketAddressIPv6 +{ + struct sockaddr_in6 address; +} _SocketAddressIPv6; + +typedef struct _SocketAddressStorage +{ + struct sockaddr_storage address; +} _SocketAddressStorage; + +typedef struct _AddressInformation +{ + struct addrinfo addr; +} _AddressInformation; + + + +//---------------------------------------------------------------------------------- +// Global module forward declarations +//---------------------------------------------------------------------------------- + +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); +static bool IsIPv4Address(const char *ip); +static bool IsIPv6Address(const char *ip); +static void * GetSocketPortPtr(struct sockaddr_storage *sa); +static void * GetSocketAddressPtr(struct sockaddr_storage *sa); +static bool IsSocketValid(Socket *sock); +static void SocketSetLastError(int err); +static int SocketGetLastError(); +static char * SocketGetLastErrorString(); +static char * SocketErrorCodeToString(int err); +static bool SocketSetDefaults(SocketConfig *config); +static bool InitSocket(Socket *sock, struct addrinfo *addr); +static bool CreateSocket(SocketConfig *config, SocketResult *outresult); +static bool SocketSetBlocking(Socket *sock); +static bool SocketSetNonBlocking(Socket *sock); +static bool SocketSetOptions(SocketConfig *config, Socket *sock); +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); + +//---------------------------------------------------------------------------------- +// Global module implementation +//---------------------------------------------------------------------------------- + +// Print socket information +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) +{ + switch (family) + { + case AF_UNSPEC: { TraceLog(LOG_DEBUG, "\tFamily: Unspecified"); + } + break; + case AF_INET: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); + TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); + } + break; + case AF_INET6: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); + TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); + } + break; + case AF_NETBIOS: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); + } + break; + default: { TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family); + } + break; + } + TraceLog(LOG_DEBUG, "\tSocket type:"); + switch (socktype) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case SOCK_STREAM: + TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); + break; + case SOCK_DGRAM: + TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); + break; + case SOCK_RAW: TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; + case SOCK_RDM: + TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); + break; + case SOCK_SEQPACKET: + TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); + break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break; + } + TraceLog(LOG_DEBUG, "\tProtocol:"); + switch (protocol) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case IPPROTO_TCP: TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; + case IPPROTO_UDP: TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break; + } +} + +// Convert network ordered socket address to human readable string (127.0.0.1) +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) +{ + static const char* ipv6[INET6_ADDRSTRLEN]; + assert(sockaddr != NULL); + assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); + switch (sockaddr->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) sockaddr); + return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) sockaddr); + return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); + } + break; + } + return NULL; +} + +// Check if the null terminated string ip is a valid IPv4 address +static bool IsIPv4Address(const char *ip) +{ + struct sockaddr_in sa; + int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); + return result != 0; +} + +// Check if the null terminated string ip is a valid IPv6 address +static bool IsIPv6Address(const char *ip) +{ + struct sockaddr_in6 sa; + int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); + return result != 0; +} + +// Return a pointer to the port from the correct address family (IPv4, or IPv6) +static void *GetSocketPortPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_port); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_port); +} + +// Return a pointer to the address from the correct address family (IPv4, or IPv6) +static void *GetSocketAddressPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_addr); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_addr); +} + +// Is the socket in a valid state? +static bool IsSocketValid(Socket *sock) +{ + if (sock != NULL) + { + return (sock->channel != INVALID_SOCKET); + } + return false; +} + +// Sets the error code that can be retrieved through the WSAGetLastError function. +static void SocketSetLastError(int err) +{ +#if PLATFORM == PLATFORM_WINDOWS + WSASetLastError(err); +#else + errno = err; +#endif +} + +// Returns the error status for the last Sockets operation that failed +static int SocketGetLastError() +{ +#if PLATFORM == PLATFORM_WINDOWS + return WSAGetLastError(); +#else + return errno; +#endif +} + +// Returns a human-readable string representing the last error message +static char *SocketGetLastErrorString() +{ + return SocketErrorCodeToString(SocketGetLastError()); +} + +// Returns a human-readable string representing the error message (err) +static char *SocketErrorCodeToString(int err) +{ +#if PLATFORM == PLATFORM_WINDOWS + static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; + sprintf(gaiStrErrorBuffer, "%ws", gai_strerror(err)); + return gaiStrErrorBuffer; +#else + return gai_strerror(err); +#endif +} + +// Set the defaults in the supplied SocketConfig if they're not already set +static bool SocketSetDefaults(SocketConfig *config) +{ + if (config->backlog_size == 0) + { + config->backlog_size = SOCKET_MAX_QUEUE_SIZE; + } + + return true; +} + +// Create the socket channel +static bool InitSocket(Socket *sock, struct addrinfo *addr) +{ + switch (sock->type) + { + case SOCKET_TCP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_STREAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_STREAM, 0); + } + break; + case SOCKET_UDP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_DGRAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_DGRAM, 0); + } + break; + default: + TraceLog(LOG_WARNING, "Invalid socket type specified."); + break; + } + return IsSocketValid(sock); +} + +// CreateSocket() - Interally called by CreateSocket() +// +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +static bool CreateSocket(SocketConfig *config, SocketResult *outresult) +{ + bool success = true; + int addrstatus; + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + outresult->socket->channel = INVALID_SOCKET; + outresult->status = RESULT_FAILURE; + + // Set the socket type + outresult->socket->type = config->type; + + // Set the hints based on information in the config + // + // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. + // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); + // Note: This causes a subsequent call to bind() to auto-fill the IP address + // of the struct sockaddr with the address of the current host. + // + SocketSetHints(config, &hints); + + // Populate address information + addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set + config->port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (addrstatus != 0) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + TraceLog(LOG_WARNING, + "Failed to get resolve host %s:%s: %s", + config->host, + config->port, + SocketGetLastErrorString()); + return (success = false); + } + else + { + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); + } + + // Walk the address information linked-list + struct addrinfo *it; + for (it = res; it != NULL; it = it->ai_next) + { + // Initialise the socket + if (!InitSocket(outresult->socket, it)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + continue; + } + + // Set socket options + if (!SocketSetOptions(config, outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + } + + if (!IsSocketValid(outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog( + LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + + if (success) + { + outresult->status = RESULT_SUCCESS; + outresult->socket->ready = 0; + outresult->socket->status = 0; + if (!(config->type == SOCKET_UDP)) + { + outresult->socket->isServer = config->server; + } + switch (res->ai_addr->sa_family) + { + case AF_INET: + { + outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc( + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memset(outresult->socket->addripv4, 0, + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memcpy(&outresult->socket->addripv4->address, + (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in)); + outresult->socket->isIPv6 = false; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + case AF_INET6: + { + outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc( + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memset(outresult->socket->addripv6, 0, + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memcpy(&outresult->socket->addripv6->address, + (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6)); + outresult->socket->isIPv6 = true; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + } + } + freeaddrinfo(res); + return success; +} + +// Set the state of the Socket sock to blocking +static bool SocketSetBlocking(Socket *sock) +{ + bool ret = true; +#if PLATFORM == PLATFORM_WINDOWS + unsigned long mode = 0; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if (!(flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in blocking mode"); + return ret; + } + + ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); +#endif + return ret; +} + +// Set the state of the Socket sock to non-blocking +static bool SocketSetNonBlocking(Socket *sock) +{ + bool ret = true; +#if PLATFORM == PLATFORM_WINDOWS + unsigned long mode = 1; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if ((flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in non-blocking mode"); + return ret; + } + ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); +#endif + return ret; +} + +// Set options specified in SocketConfig to Socket sock +static bool SocketSetOptions(SocketConfig *config, Socket *sock) +{ + for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) + { + SocketOpt *opt = &config->sockopts[i]; + if (opt->id == 0) + { + break; + } + + if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) + { + return false; + } + } + + return true; +} + +// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) +{ + if (config == NULL || hints == NULL) + { + return; + } + memset(hints, 0, sizeof(*hints)); + + // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address + if (IsIPv4Address(config->host)) + { + hints->ai_family = AF_INET; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + if (IsIPv6Address(config->host)) + { + hints->ai_family = AF_INET6; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + hints->ai_family = AF_UNSPEC; + } + } + + if (config->type == SOCKET_UDP) + { + hints->ai_socktype = SOCK_DGRAM; + } + else + { + hints->ai_socktype = SOCK_STREAM; + } + + // Set passive unless UDP client + if (!(config->type == SOCKET_UDP) || config->server) + { + hints->ai_flags = AI_PASSIVE; + } +} + +//---------------------------------------------------------------------------------- +// Module implementation +//---------------------------------------------------------------------------------- + +// Initialise the network (requires for windows platforms only) +bool InitNetwork() +{ +#if PLATFORM == PLATFORM_WINDOWS + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + return false; + } + else + { + TraceLog(LOG_INFO, "WinSock initialised."); + } + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + WSACleanup(); + return false; + } + + return true; +#else + return true; +#endif +} + +// Cleanup, and close the network +void CloseNetwork() +{ +#if PLATFORM == PLATFORM_WINDOWS + WSACleanup(); +#endif +} + +// Protocol-independent name resolution from an address to an ANSI host name +// and from a port number to the ANSI service name. +// +// The flags parameter can be used to customize processing of the getnameinfo function +// +// The following flags are available: +// +// NAME_INFO_DEFAULT 0x00 // No flags set +// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts +// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address +// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS +// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) +// NAME_INFO_DGRAM 0x10 // Service is a datagram service +void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + + // Populate address information + status = getaddrinfo(ip, // e.g. "www.example.com" or IP + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); + } + else + { + TraceLog(LOG_DEBUG, "Resolving... %s::%s", ip, port); + } + + // Attempt to resolve network byte order ip to hostname + switch (res->ai_family) + { + case AF_INET: + status = getnameinfo(&*((struct sockaddr *) res->ai_addr), + sizeof(*((struct sockaddr_in *) res->ai_addr)), + host, + NI_MAXHOST, + serv, + NI_MAXSERV, + flags); + break; + case AF_INET6: + status = getnameinfo(&*((struct sockaddr_in6 *) res->ai_addr), + sizeof(*((struct sockaddr_in6 *) res->ai_addr)), + host, + NI_MAXHOST, + serv, + NI_MAXSERV, + flags); + break; + default: break; + } + + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); + } + else + { + TraceLog(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); +} + +// Protocol-independent translation from an ANSI host name to an address +// +// e.g. +// const char* address = "127.0.0.1" (local address) +// const char* port = "80" +// +// Parameters: +// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. +// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. +// +// Returns: +// The total amount of addresses found, -1 on error +// +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // will point to the results + struct addrinfo *iterator; + assert(((address != NULL || address != 0) || (service != NULL || service != 0))); + assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + hints.ai_flags = flags; + assert(hints.ai_addrlen == NULL || hints.ai_addrlen == 0); + assert(hints.ai_canonname == NULL || hints.ai_canonname == 0); + assert(hints.ai_addr == NULL || hints.ai_addr == 0); + assert(hints.ai_next == NULL || hints.ai_next == 0); + + // When the address is NULL, populate the IP for me + if (address == NULL) + { + if ((hints.ai_flags & AI_PASSIVE) == 0) + { + hints.ai_flags |= AI_PASSIVE; + } + } + + TraceLog(LOG_INFO, "Resolving host..."); + + // Populate address information + status = getaddrinfo(address, // e.g. "www.example.com" or IP + service, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + int error = SocketGetLastError(); + SocketSetLastError(0); + TraceLog(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); + return -1; + } + else + { + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, service); + } + + // Calculate the size of the address information list + int size = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + size++; + } + + // Validate the size is > 0, otherwise return + if (size <= 0) + { + TraceLog(LOG_WARNING, "Error, no addresses found."); + return -1; + } + + // If not address list was allocated, allocate it dynamically with the known address size + if (outAddr == NULL) + { + outAddr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + } + + // Dynamically allocate an array of address information structs + if (outAddr != NULL) + { + int i; + for (i = 0; i < size; ++i) + { + outAddr[i] = AllocAddress(); + if (outAddr[i] == NULL) + { + break; + } + } + outAddr[i] = NULL; + if (i != size) + { + outAddr = NULL; + } + } + else + { + TraceLog(LOG_WARNING, + "Error, failed to dynamically allocate memory for the address list"); + return -1; + } + + // Copy all the address information from res into outAddrList + int i = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + if (i < size) + { + outAddr[i]->addr.ai_flags = iterator->ai_flags; + outAddr[i]->addr.ai_family = iterator->ai_family; + outAddr[i]->addr.ai_socktype = iterator->ai_socktype; + outAddr[i]->addr.ai_protocol = iterator->ai_protocol; + outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; + *outAddr[i]->addr.ai_addr = *iterator->ai_addr; +#if NET_DEBUG_ENABLED + TraceLog(LOG_DEBUG, "GetAddressInformation"); + TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); + PrintSocket(outAddr[i]->addr.ai_addr, + outAddr[i]->addr.ai_family, + outAddr[i]->addr.ai_socktype, + outAddr[i]->addr.ai_protocol); + TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); + TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); +#endif + i++; + } + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); + + // Return the total count of addresses found + return size; +} + +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +bool SocketCreate(SocketConfig *config, SocketResult *result) +{ + // Socket creation result + bool success = true; + + // Make sure we've not received a null config or result pointer + if (config == NULL || result == NULL) + { + return (success = false); + } + + // Set the defaults based on the config + if (!SocketSetDefaults(config)) + { + TraceLog(LOG_WARNING, "Configuration Error."); + success = false; + } + else + { + // Create the socket + if (CreateSocket(config, result)) + { + if (config->nonblocking) + { + SocketSetNonBlocking(result->socket); + } + else + { + SocketSetBlocking(result->socket); + } + } + else + { + success = false; + } + } + return success; +} + +// Bind a socket to a local address +// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. +bool SocketBind(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + struct sockaddr_storage *sock_addr = NULL; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot bind to socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + if (result->socket->isIPv6) + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address; + } + else + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address; + } + if (sock_addr != NULL) + { + if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Successfully bound socket."); + success = true; + } + else + { + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + success = false; + } + } + } + // Was the bind a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + socklen_t sock_len = sizeof(*sock_addr); + if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0) + { + TraceLog(LOG_WARNING, "Couldn't get socket address"); + } + else + { + struct sockaddr_in *s = (struct sockaddr_in *) sock_addr; + // result->socket->address.host = s->sin_addr.s_addr; + // result->socket->address.port = s->sin_port; + + // + result->socket->addripv4 + = (struct _SocketAddressIPv4 *) malloc(sizeof(*result->socket->addripv4)); + if (result->socket->addripv4 != NULL) + { + memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); + } + memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + // + } + } + return success; +} + +// Listens (and queues) incoming connections requests for a bound port. +bool SocketListen(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + // Don't listen on UDP sockets + if (!(config->type == SOCKET_UDP)) + { + if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Started listening on socket..."); + success = true; + } + else + { + success = false; + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + } + } + else + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); + success = false; + } + } + + // Was the listen a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + return success; +} + +// Connect the socket to the destination specified by "host" and "port" in SocketConfig +bool SocketConnect(SocketConfig *config, SocketResult *result) +{ + bool success = true; + result->status = RESULT_FAILURE; + + // Only bind to sockets marked as server + if (config->server) + { + TraceLog(LOG_WARNING, + "Cannot connect to socket marked as \"Server\" in SocketConfig."); + success = false; + } + else + { + if (IsIPv4Address(config->host)) + { + struct sockaddr_in ip4addr; + ip4addr.sin_family = AF_INET; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip4addr.sin_port = htons(hport); + inet_pton(AF_INET, config->host, &ip4addr.sin_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + else + { + if (IsIPv6Address(config->host)) + { + struct sockaddr_in6 ip6addr; + ip6addr.sin6_family = AF_INET6; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip6addr.sin6_port = htons(hport); + inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + } + } + + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Closes an existing socket +// +// SocketChannel socket - The id of the socket to close +void SocketClose(Socket *sock) +{ + if (sock != NULL) + { + if (sock->channel != INVALID_SOCKET) + { + closesocket(sock->channel); + } + } +} + +// Returns the sockaddress for a specific socket in a generic storage struct +SocketAddressStorage SocketGetPeerAddress(Socket *sock) +{ + if (sock->isServer) + { + return NULL; + } + if (sock->isIPv6) + { + return sock->addripv6; + } + else + { + return sock->addripv4; + } +} + +// Return the address-type appropriate host portion of a socket address +char *GetSocketAddressHost(SocketAddressStorage storage) +{ + assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); + return SocketAddressToString((struct sockaddr_storage *) storage); +} + +// Return the address-type appropriate port(service) portion of a socket address +short GetSocketAddressPort(SocketAddressStorage storage) +{ + return ntohs(GetSocketPortPtr(storage)); +} + +// The accept function permits an incoming connection attempt on a socket. +// +// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) +// SocketResult* out - The result of this function (if any, including errors) +// +// e.g. +// +// SocketResult connection; +// bool connected = false; +// if (!connected) +// { +// if (SocketAccept(server_res.socket.channel, &connection)) +// { +// connected = true; +// } +// } +Socket *SocketAccept(Socket *server, SocketConfig *config) +{ + if (!server->isServer || server->type == SOCKET_UDP) + { + return NULL; + } + struct sockaddr_storage sock_addr; + socklen_t sock_alen; + Socket * sock; + sock = AllocSocket(); + server->ready = 0; + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *) &sock_addr, &sock_alen); + if (sock->channel == INVALID_SOCKET) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + SocketClose(sock); + return NULL; + } + (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); + sock->isServer = false; + sock->ready = 0; + sock->type = server->type; + switch (sock_addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr); + sock->addripv4 = (struct _SocketAddressIPv4 *) malloc(sizeof(*sock->addripv4)); + if (sock->addripv4 != NULL) + { + memset(sock->addripv4, 0, sizeof(*sock->addripv4)); + memcpy(&sock->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv4->address.sin_port)); + } + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) &sock_addr); + sock->addripv6 = (struct _SocketAddressIPv6 *) malloc(sizeof(*sock->addripv6)); + if (sock->addripv6 != NULL) + { + memset(sock->addripv6, 0, sizeof(*sock->addripv6)); + memcpy(&sock->addripv6->address, (struct sockaddr_in6 *) &s->sin6_addr, sizeof(struct sockaddr_in6)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv6->address.sin6_port)); + } + } + break; + } + return sock; +} + +// Verify that the channel is in the valid range +static int ValidChannel(int channel) +{ + if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) + { + TraceLog(LOG_WARNING, "Invalid channel"); + return 0; + } + return 1; +} + +// Set the socket channel +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) +{ + struct UDPChannel *binding; + if (socket == NULL) + { + TraceLog(LOG_WARNING, "Passed a NULL socket"); + return (-1); + } + if (channel == -1) + { + for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) + { + binding = &socket->binding[channel]; + if (binding->numbound < SOCKET_MAX_UDPADDRESSES) + { + break; + } + } + } + else + { + if (!ValidChannel(channel)) + { + return (-1); + } + binding = &socket->binding[channel]; + } + if (binding->numbound == SOCKET_MAX_UDPADDRESSES) + { + TraceLog(LOG_WARNING, "No room for new addresses"); + return (-1); + } + binding->address[binding->numbound++] = *address; + return (channel); +} + +// Remove the socket channel +void SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) + { + socket->binding[channel].numbound = 0; + } +} + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +SocketDataPacket *AllocPacket(int size) +{ + SocketDataPacket *packet; + int error; + + error = 1; + packet = (SocketDataPacket *) malloc(sizeof(*packet)); + if (packet != NULL) + { + packet->maxlen = size; + packet->data = (uint8_t *) malloc(size); + if (packet->data != NULL) + { + error = 0; + } + } + if (error) + { + FreePacket(packet); + packet = NULL; + } + return (packet); +} + +int ResizePacket(SocketDataPacket *packet, int newsize) +{ + uint8_t *newdata; + + newdata = (uint8_t *) malloc(newsize); + if (newdata != NULL) + { + free(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + return (packet->maxlen); +} + +void FreePacket(SocketDataPacket *packet) +{ + if (packet) + { + free(packet->data); + free(packet); + } +} + +/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, + each 'size' bytes long. + A pointer to the packet array is returned, or NULL if the function ran out + of memory. + */ +SocketDataPacket **AllocPacketList(int howmany, int size) +{ + SocketDataPacket **packetV; + + packetV = (SocketDataPacket **) malloc((howmany + 1) * sizeof(*packetV)); + if (packetV != NULL) + { + int i; + for (i = 0; i < howmany; ++i) + { + packetV[i] = AllocPacket(size); + if (packetV[i] == NULL) + { + break; + } + } + packetV[i] = NULL; + + if (i != howmany) + { + FreePacketList(packetV); + packetV = NULL; + } + } + return (packetV); +} + +void FreePacketList(SocketDataPacket **packetV) +{ + if (packetV) + { + int i; + for (i = 0; packetV[i]; ++i) + { + FreePacket(packetV[i]); + } + free(packetV); + } +} + +// Send 'len' bytes of 'data' over the non-server socket 'sock' +// +// Example +int SocketSend(Socket *sock, const void *datap, int length) +{ + int sent = 0; + int left = length; + int status = -1; + int numsent = 0; + const unsigned char *data = (const unsigned char *) datap; + + // Server sockets are for accepting connections only + if (sock->isServer) + { + TraceLog(LOG_WARNING, "Cannot send information on a server socket"); + return -1; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + length = send(sock->channel, (const char *) data, left, 0); + if (length > 0) + { + sent += length; + left -= length; + data += length; + } + } while ((left > 0) && // While we still have bytes left to send + ((length > 0) || // The amount of bytes we actually sent is > 0 + (SocketGetLastError() == WSAEINTR)) // The socket was interupted + ); + + if (length == SOCKET_ERROR) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else + { + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); + } + + return sent; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + if (sock->isIPv6) + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv6->address, + sizeof(sock->addripv6->address)); + } + else + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv4->address, + sizeof(sock->addripv4->address)); + } + if (sent >= 0) + { + sock->status = 0; + ++numsent; + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); + } + else + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); + SocketSetLastError(0); + return 0; + } + return numsent; + } + break; + default: break; + } + return -1; +} + +// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', +// and store them in the buffer pointed to by 'data'. +// This function returns the actual amount of data received. If the return +// value is less than or equal to zero, then either the remote connection was +// closed, or an unknown socket error occurred. +int SocketReceive(Socket *sock, void *data, int maxlen) +{ + int len = 0; + int numrecv = 0; + int status = 0; + socklen_t sock_len; + struct sockaddr_storage sock_addr; + char ip[INET6_ADDRSTRLEN]; + + // Server sockets are for accepting connections only + if (sock->isServer && sock->type == SOCKET_TCP) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", + "Server sockets cannot be used to receive data"); + SocketSetLastError(0); + return 0; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + len = recv(sock->channel, (char *) data, maxlen, 0); + } while (SocketGetLastError() == WSAEINTR); + + if (len > 0) + { + // Who sent the packet? + if (sock->type == SOCKET_UDP) + { + TraceLog( + LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *) &sock_addr), ip, sizeof(ip))); + } + ((unsigned char *) data)[len] = '\0'; // Add null terminating character to the end of the stream + TraceLog(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); + } + sock->ready = 0; + return len; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + sock_len = sizeof(sock_addr); + status = recvfrom(sock->channel, // The receving channel + data, // A pointer to the data buffer to fill + maxlen, // The max length of the data to fill + 0, // Flags + (struct sockaddr *) &sock_addr, // The address of the recevied data + &sock_len // The length of the received data address + ); + if (status >= 0) + { + ++numrecv; + } + else + { + sock->status = SocketGetLastError(); + switch (sock->status) + { + case WSAEWOULDBLOCK: { break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + break; + } + } + SocketSetLastError(0); + return 0; + } + sock->ready = 0; + return numrecv; + } + break; + } + return -1; +} + +// Does the socket have it's 'ready' flag set? +bool IsSocketReady(Socket *sock) +{ + return (sock != NULL) && (sock->ready); +} + +// Check if the socket is considered connected +bool IsSocketConnected(Socket *sock) +{ +#if PLATFORM_WINDOWS + FD_SET writefds; + FD_ZERO(&writefds); + FD_SET(sock->channel, &writefds); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 1000000000UL; + int total = select(0, NULL, &writefds, NULL, &timeout); + if (total == -1) + { // Error + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else if (total == 0) + { // Timeout + return false; + } + else + { + if (FD_ISSET(sock->channel, &writefds)) + { + return true; + } + } + return false; +#else + return true; +#endif +} + +// Allocate and return a SocketResult struct +SocketResult *AllocSocketResult() +{ + struct SocketResult *res; + res = (struct SocketResult *) malloc(sizeof(*res)); + if (res != NULL) + { + memset(res, 0, sizeof(*res)); + if ((res->socket = AllocSocket()) == NULL) + { + free(res); + res = NULL; + } + } + return res; +} + +// Free an allocated SocketResult +void FreeSocketResult(SocketResult **result) +{ + if (*result != NULL) + { + if ((*result)->socket != NULL) + { + FreeSocket(&((*result)->socket)); + } + free(*result); + *result = NULL; + } +} + +// Allocate a Socket +Socket *AllocSocket() +{ + // Allocate a socket if one already hasn't been + struct Socket *sock; + sock = (Socket *) malloc(sizeof(*sock)); + if (sock != NULL) + { + memset(sock, 0, sizeof(*sock)); + } + else + { + TraceLog( + LOG_WARNING, "Ran out of memory attempting to allocate a socket"); + SocketClose(sock); + free(sock); + sock = NULL; + } + return sock; +} + +// Free an allocated Socket +void FreeSocket(Socket **sock) +{ + if (*sock != NULL) + { + free(*sock); + *sock = NULL; + } +} + +// Allocate a SocketSet +SocketSet *AllocSocketSet(int max) +{ + struct SocketSet *set; + int i; + + set = (struct SocketSet *) malloc(sizeof(*set)); + if (set != NULL) + { + set->numsockets = 0; + set->maxsockets = max; + set->sockets = (struct Socket **) malloc(max * sizeof(*set->sockets)); + if (set->sockets != NULL) + { + for (i = 0; i < max; ++i) + { + set->sockets[i] = NULL; + } + } + else + { + free(set); + set = NULL; + } + } + return (set); +} + +// Free an allocated SocketSet +void FreeSocketSet(SocketSet *set) +{ + if (set) + { + free(set->sockets); + free(set); + } +} + +// Add a Socket "sock" to the SocketSet "set" +int AddSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + if (set->numsockets == set->maxsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); + SocketSetLastError(0); + return (-1); + } + set->sockets[set->numsockets++] = (struct Socket *) sock; + } + else + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket was null"); + SocketSetLastError(0); + return (-1); + } + return (set->numsockets); +} + +// Remove a Socket "sock" to the SocketSet "set" +int RemoveSocket(SocketSet *set, Socket *sock) +{ + int i; + + if (sock != NULL) + { + for (i = 0; i < set->numsockets; ++i) + { + if (set->sockets[i] == (struct Socket *) sock) + { + break; + } + } + if (i == set->numsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket not found"); + SocketSetLastError(0); + return (-1); + } + --set->numsockets; + for (; i < set->numsockets; ++i) + { + set->sockets[i] = set->sockets[i + 1]; + } + } + return (set->numsockets); +} + +// Check the sockets in the socket set for pending information +int CheckSockets(SocketSet *set, unsigned int timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for (i = set->numsockets - 1; i >= 0; --i) + { + if (set->sockets[i]->channel > maxfd) + { + maxfd = set->sockets[i]->channel; + } + } + + // Check the file descriptors for available data + do + { + SocketSetLastError(0); + + // Set up the mask of file descriptors + FD_ZERO(&mask); + for (i = set->numsockets - 1; i >= 0; --i) + { + FD_SET(set->sockets[i]->channel, &mask); + } // Set up the timeout + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + /* Look! */ + retval = select(maxfd + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + // Mark all file descriptors ready that have data available + if (retval > 0) + { + for (i = set->numsockets - 1; i >= 0; --i) + { + if (FD_ISSET(set->sockets[i]->channel, &mask)) + { + set->sockets[i]->ready = 1; + } + } + } + return (retval); +} + +// Allocate an AddressInformation +AddressInformation AllocAddress() +{ + AddressInformation addressInfo = NULL; + addressInfo = (AddressInformation) calloc(1, sizeof(*addressInfo)); + if (addressInfo != NULL) + { + addressInfo->addr.ai_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr)); + if (addressInfo->addr.ai_addr == NULL) + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct sockaddr\""); + } + } + else + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct AddressInformation\""); + } + return addressInfo; +} + +// Free an AddressInformation struct +void FreeAddress(AddressInformation *addressInfo) +{ + if (*addressInfo != NULL) + { + if ((*addressInfo)->addr.ai_addr != NULL) + { + free((*addressInfo)->addr.ai_addr); + (*addressInfo)->addr.ai_addr = NULL; + } + free(*addressInfo); + *addressInfo = NULL; + } +} + +// Allocate a list of AddressInformation +AddressInformation *AllocAddressList(int size) +{ + AddressInformation *addr; + addr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + return addr; +} + +// Opaque datatype accessor addrinfo->ai_family +int GetAddressFamily(AddressInformation address) +{ + return address->addr.ai_family; +} + +// Opaque datatype accessor addrinfo->ai_socktype +int GetAddressSocketType(AddressInformation address) +{ + return address->addr.ai_socktype; +} + +// Opaque datatype accessor addrinfo->ai_protocol +int GetAddressProtocol(AddressInformation address) +{ + return address->addr.ai_protocol; +} + +// Opaque datatype accessor addrinfo->ai_canonname +char *GetAddressCanonName(AddressInformation address) +{ + return address->addr.ai_canonname; +} + +// Opaque datatype accessor addrinfo->ai_addr +char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport) +{ + char * ip[INET6_ADDRSTRLEN]; + char * result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr; + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr); + result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); + *outport = ntohs(s->sin_port); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr); + result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); + *outport = ntohs(s->sin6_port); + } + break; + } + if (result == NULL) + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + printf("Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + printf("Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + printf("Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + printf("Network: 0x%016" PRIX64 " - %" PRIu64 "\n", + (uint64_t)(packet->data - packet->offs), + (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + return value; +} diff --git a/src/rnet.h b/src/rnet.h new file mode 100644 index 00000000..c2a14015 --- /dev/null +++ b/src/rnet.h @@ -0,0 +1,228 @@ +/********************************************************************************************** +* +* rnet - Provides cross-platform network defines, macros etc +* +* DEPENDENCIES: +* - Used for cross-platform type specifiers +* +* INSPIRED BY: +* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php +* SDL_net - https://www.libsdl.org/projects/SDL_net/ +* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html +* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html +* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2 +* +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019): +* - Initial version +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Platform type sizes +//---------------------------------------------------------------------------------- + +#include + +//---------------------------------------------------------------------------------- +// Undefine any conflicting windows.h symbols +//---------------------------------------------------------------------------------- + +// If defined, the following flags inhibit definition of the indicated items. +#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ +#define NOVIRTUALKEYCODES // VK_* +#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* +#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* +#define NOSYSMETRICS // SM_* +#define NOMENUS // MF_* +#define NOICONS // IDI_* +#define NOKEYSTATES // MK_* +#define NOSYSCOMMANDS // SC_* +#define NORASTEROPS // Binary and Tertiary raster ops +#define NOSHOWWINDOW // SW_* +#define OEMRESOURCE // OEM Resource values +#define NOATOM // Atom Manager routines +#define NOCLIPBOARD // Clipboard routines +#define NOCOLOR // Screen colors +#define NOCTLMGR // Control and Dialog routines +#define NODRAWTEXT // DrawText() and DT_* +#define NOGDI // All GDI defines and routines +#define NOKERNEL // All KERNEL defines and routines +#define NOUSER // All USER defines and routines +#define NONLS // All NLS defines and routines +#define NOMB // MB_* and MessageBox() +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMETAFILE // typedef METAFILEPICT +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOMSG // typedef MSG and associated routines +#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* +#define NOSCROLL // SB_* and scrolling routines +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOSOUND // Sound driver routines +#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines +#define NOWH // SetWindowsHook and WH_* +#define NOWINOFFSETS // GWL_*, GCL_*, associated routines +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOHELP // Help engine interface. +#define NOPROFILER // Profiler interface. +#define NODEFERWINDOWPOS // DeferWindowPos routines +#define NOMCX // Modem Configuration Extensions +#define MMNOSOUND + +//---------------------------------------------------------------------------------- +// Platform defines +//---------------------------------------------------------------------------------- + +#define PLATFORM_WINDOWS 1 +#define PLATFORM_LINUX 2 + +#if defined(__WIN32__) || defined(WIN32) +# define PLATFORM PLATFORM_WINDOWS +#elif defined(_LINUX) +# define PLATFORM PLATFORM_LINUX +#endif + +//---------------------------------------------------------------------------------- +// Platform type definitions +// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h +//---------------------------------------------------------------------------------- + +#ifdef WIN32 +typedef int socklen_t; +#endif + +#ifndef RESULT_SUCCESS +# define RESULT_SUCCESS 0 +#endif // RESULT_SUCCESS + +#ifndef RESULT_FAILURE +# define RESULT_FAILURE 1 +#endif // RESULT_FAILURE + +#ifndef htonll +# ifdef _BIG_ENDIAN +# define htonll(x) (x) +# define ntohll(x) (x) +# else +# define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32)) +# define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32)) +# endif // _BIG_ENDIAN +#endif // htonll + +//---------------------------------------------------------------------------------- +// Platform specific network includes +// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h +//---------------------------------------------------------------------------------- + +// Include system network headers + +#ifdef _WIN32 +# pragma comment(lib, "ws2_32.lib") +# define __USE_W32_SOCKETS +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# define IPTOS_LOWDELAY 0x10 +#else /* UNIX */ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif /* WIN32 */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET ~(0) +#endif + +#ifndef __USE_W32_SOCKETS +# define closesocket close +# define SOCKET int +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 +#endif + +#ifdef __USE_W32_SOCKETS +# ifndef EINTR +# define EINTR WSAEINTR +# endif +#endif + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +// Network connection related defines +#define SOCKET_MAX_SET_SIZE (32) // Maximum sockets in a set +#define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size + +// Network address related defines +#define ADDRESS_IPV4_ADDRSTRLEN (22) // IPv4 string length +#define ADDRESS_IPV6_ADDRSTRLEN (65) // IPv6 string length +#define ADDRESS_TYPE_ANY (0) // AF_UNSPEC +#define ADDRESS_TYPE_IPV4 (2) // AF_INET +#define ADDRESS_TYPE_IPV6 (23) // AF_INET6 +#define ADDRESS_MAXHOST (1025) // Max size of a fully-qualified domain name +#define ADDRESS_MAXSERV (32) // Max size of a service name + +// Network address related defines +#define ADDRESS_ANY ((unsigned long) 0x00000000) +#define ADDRESS_LOOPBACK (0x7f000001) +#define ADDRESS_BROADCAST ((unsigned long) 0xffffffff) +#define ADDRESS_NONE (0xffffffff) + +// Address resolution related defines +#if defined(_WIN32) + #define ADDRESS_INFO_PASSIVE (0x00000001) // Socket address will be used in bind() call + #define ADDRESS_INFO_CANONNAME (0x00000002) // Return canonical name in first ai_canonname + #define ADDRESS_INFO_NUMERICHOST (0x00000004) // Nodename must be a numeric address string + #define ADDRESS_INFO_NUMERICSERV (0x00000008) // Servicename must be a numeric port number + #define ADDRESS_INFO_DNS_ONLY (0x00000010) // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) + #define ADDRESS_INFO_ALL (0x00000100) // Query both IP6 and IP4 with AI_V4MAPPED + #define ADDRESS_INFO_ADDRCONFIG (0x00000400) // Resolution only if global address configured + #define ADDRESS_INFO_V4MAPPED (0x00000800) // On v6 failure, query v4 and convert to V4MAPPED format + #define ADDRESS_INFO_NON_AUTHORITATIVE (0x00004000) // LUP_NON_AUTHORITATIVE + #define ADDRESS_INFO_SECURE (0x00008000) // LUP_SECURE + #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000) // LUP_RETURN_PREFERRED_NAMES + #define ADDRESS_INFO_FQDN (0x00020000) // Return the FQDN in ai_canonname + #define ADDRESS_INFO_FILESERVER (0x00040000) // Resolving fileserver name resolution + #define ADDRESS_INFO_DISABLE_IDN_ENCODING (0x00080000) // Disable Internationalized Domain Names handling + #define ADDRESS_INFO_EXTENDED (0x80000000) // Indicates this is extended ADDRINFOEX(2/..) struct + #define ADDRESS_INFO_RESOLUTION_HANDLE (0x40000000) // Request resolution handle +#endif + +// Network resolution related defines +#define NAME_INFO_DEFAULT (0x00) // No flags set +#define NAME_INFO_NOFQDN (0x01) // Only return nodename portion for local hosts +#define NAME_INFO_NUMERICHOST (0x02) // Return numeric form of the host's address +#define NAME_INFO_NAMEREQD (0x04) // Error if the host's name not in DNS +#define NAME_INFO_NUMERICSERV (0x08) // Return numeric form of the service (port #) +#define NAME_INFO_DGRAM (0x10) // Service is a datagram service \ No newline at end of file -- cgit v1.2.3 From 0c567cd259285fb33b3e2ab514c48322da0a0000 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 23 Apr 2019 18:10:38 +0200 Subject: WARNING: Issues on web building Found some issues when building for web using latest emscripten 1.38.30, traced the error and found that eglGetProcAdress does not return function pointers for VAO functionality, supported by extension. It requires more investigation but now it works (avoiding VAO usage) --- examples/Makefile | 9 +++++---- src/Makefile | 2 +- src/core.c | 3 ++- src/external/cgltf.h | 4 ++-- src/external/miniaudio.h | 2 -- src/rlgl.h | 12 ++++++++++++ templates/web_shell/shell.html | 10 +++++----- 7 files changed, 27 insertions(+), 15 deletions(-) (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile index 2ceb3f7d..a1d45ef6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -25,7 +25,7 @@ # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 2.0.0 +RAYLIB_VERSION ?= 2.5.0 RAYLIB_API_VERSION ?= 1 RAYLIB_PATH ?= .. @@ -118,8 +118,8 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk - EMSCRIPTEN_VERSION = 1.38.21 - CLANG_VERSION = e1.38.21_64bit + EMSCRIPTEN_VERSION = 1.38.30 + CLANG_VERSION = e1.38.30_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 NODE_VERSION = 8.9.1_64bit 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) @@ -249,7 +249,8 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s EMTERPRETIFY_ASYNC=1 # support synchronous loops by emterpreter # --profiling # include information for code profiling # --preload-file resources # specify a resources folder for data compilation - CFLAGS += -Os -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 + CFLAGS += -Os -s USE_GLFW=3 -s ASSERTIONS=2 -s WASM=1 + # -Os -s WASM=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 # NOTE: Simple raylib examples are compiled to be interpreter by emterpreter, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger diff --git a/src/Makefile b/src/Makefile index 997f041c..ea09aa9f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -42,7 +42,7 @@ .PHONY: all clean install uninstall # Define required raylib variables -RAYLIB_VERSION = 2.4.0 +RAYLIB_VERSION = 2.5.0 RAYLIB_API_VERSION = 2 # See below for alternatives. diff --git a/src/core.c b/src/core.c index 844994d0..5ec3b76a 100644 --- a/src/core.c +++ b/src/core.c @@ -3172,7 +3172,8 @@ static void PollInputEvents(void) // NOTE: GLFW3 joystick functionality not available in web #if defined(PLATFORM_WEB) // Get number of gamepads connected - int numGamepads = emscripten_get_num_gamepads(); + int numGamepads = 0; + if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) { diff --git a/src/external/cgltf.h b/src/external/cgltf.h index 4302e77b..85d5c985 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -369,7 +369,7 @@ typedef struct cgltf_light { cgltf_float spot_outer_cone_angle; } cgltf_light; -typedef struct cgltf_node { +struct cgltf_node { char* name; cgltf_node* parent; cgltf_node** children; @@ -388,7 +388,7 @@ typedef struct cgltf_node { cgltf_float rotation[4]; cgltf_float scale[3]; cgltf_float matrix[16]; -} cgltf_node; +}; typedef struct cgltf_scene { char* name; diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index a5646c71..dae605f2 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -21915,8 +21915,6 @@ extern "C" { #endif EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { - ma_result result; - if (pDevice->type == ma_device_type_duplex) { ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB); } else { diff --git a/src/rlgl.h b/src/rlgl.h index dd2929ca..71a1dc4b 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1559,7 +1559,19 @@ void rlglInit(int width, int height) glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted + + if (glGenVertexArrays == NULL) printf("glGenVertexArrays is NULL.\n"); // WEB: ISSUE FOUND! ...but why? + if (glBindVertexArray == NULL) printf("glBindVertexArray is NULL.\n"); // WEB: ISSUE FOUND! ...but why? } + + // TODO: HACK REVIEW! + // For some reason on raylib 2.5, VAO usage breaks the build + // error seems related to function pointers but I can not get detailed info... + // Avoiding VAO usage is the only solution for now... :( + // Ref: https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html + #if defined(PLATFORM_WEB) + vaoSupported = false; + #endif // Check NPOT textures support // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature diff --git a/templates/web_shell/shell.html b/templates/web_shell/shell.html index f158c432..2e891461 100644 --- a/templates/web_shell/shell.html +++ b/templates/web_shell/shell.html @@ -178,14 +178,14 @@ } + + + {{{ SCRIPT }}} + + \ No newline at end of file diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 784c7dca..95fb6f6f 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\templates\web_shell\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html EXT = .html endif diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index a1e65772..ea732c92 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\templates\web_shell\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html EXT = .html endif diff --git a/templates/standard_game/Makefile b/templates/standard_game/Makefile index bd7906b8..56b76b42 100644 --- a/templates/standard_game/Makefile +++ b/templates/standard_game/Makefile @@ -230,7 +230,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\templates\web_shell\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html EXT = .html endif diff --git a/templates/web_shell/shell.html b/templates/web_shell/shell.html deleted file mode 100644 index 641cfc02..00000000 --- a/templates/web_shell/shell.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - raylib HTML5 GAME - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
Downloading...
- - - - - -
- -
-
- -
- -
- - - - - - - {{{ SCRIPT }}} - - \ No newline at end of file -- cgit v1.2.3 From b7d48567453ed7abd8fb1a8ca89c45fafb3a4101 Mon Sep 17 00:00:00 2001 From: flashback-fx Date: Fri, 24 May 2019 19:10:14 +0000 Subject: Add easings example --- examples/core/easings_example.c | 354 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 examples/core/easings_example.c (limited to 'examples') diff --git a/examples/core/easings_example.c b/examples/core/easings_example.c new file mode 100644 index 00000000..d42b3c33 --- /dev/null +++ b/examples/core/easings_example.c @@ -0,0 +1,354 @@ +/******************************************************************************************* +* +* raylib [easings] example +* +* 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) +* +* Copyright (c) 2019 Juan Miguel López +* +********************************************************************************************/ + + +#include +#include "easings.h" + + +// Application constants +#define SCR_WIDTH 800 +#define SCR_HEIGHT 450 +#define BALL_RADIUS 16.0f +#define BALL_COLOR MAROON +#define PAD 80.0f +#define START_X (0.0f + (BALL_RADIUS) + (PAD)) +#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) +#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) +#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) +#define T_ADVANCE 1.0f +#define D_DFT 300.0f +#define TARGET_FPS 60 +#define BG_COLOR RAYWHITE +#define TEXT_COLOR LIGHTGRAY +#define FONT_SIZE 20 +#define D_STEP 20.0f +#define D_STEP_FINE 2.0f +#define D_MIN 1.0f +#define D_MAX 10000.0f + +// Application control keys +#define KEY_NEXT_EASE_X KEY_RIGHT +#define KEY_PREV_EASE_X KEY_LEFT +#define KEY_NEXT_EASE_Y KEY_DOWN +#define KEY_PREV_EASE_Y KEY_UP +#define KEY_INCR_D_STEP KEY_W +#define KEY_DECR_D_STEP KEY_Q +#define KEY_INCR_D_FINE KEY_S +#define KEY_DECR_D_FINE KEY_A +#define KEY_PLAY_PAUSE KEY_ENTER +#define KEY_RESTART KEY_SPACE +#define KEY_TOGGLE_UNBOUNDED_T KEY_T + + +// Easing types +enum EasingTypes { + EASE_LINEAR_NONE, + EASE_LINEAR_IN, + EASE_LINEAR_OUT, + EASE_LINEAR_IN_OUT, + EASE_SINE_IN, + EASE_SINE_OUT, + EASE_SINE_IN_OUT, + EASE_CIRC_IN, + EASE_CIRC_OUT, + EASE_CIRC_IN_OUT, + EASE_CUBIC_IN, + EASE_CUBIC_OUT, + EASE_CUBIC_IN_OUT, + EASE_QUAD_IN, + EASE_QUAD_OUT, + EASE_QUAD_IN_OUT, + EASE_EXPO_IN, + EASE_EXPO_OUT, + EASE_EXPO_IN_OUT, + EASE_BACK_IN, + EASE_BACK_OUT, + EASE_BACK_IN_OUT, + EASE_BOUNCE_OUT, + EASE_BOUNCE_IN, + EASE_BOUNCE_IN_OUT, + EASE_ELASTIC_IN, + EASE_ELASTIC_OUT, + EASE_ELASTIC_IN_OUT, + NUM_EASING_TYPES, + EASING_NONE = NUM_EASING_TYPES +}; + + +static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis + + +// Easing functions reference data +static const struct { + const char *name; + float (*func)(float, float, float, float); +} Easings[] = { + [EASE_LINEAR_NONE] = { + .name = "EaseLinearNone", + .func = EaseLinearNone, + }, + [EASE_LINEAR_IN] = { + .name = "EaseLinearIn", + .func = EaseLinearIn, + }, + [EASE_LINEAR_OUT] = { + .name = "EaseLinearOut", + .func = EaseLinearOut, + }, + [EASE_LINEAR_IN_OUT] = { + .name = "EaseLinearInOut", + .func = EaseLinearInOut, + }, + [EASE_SINE_IN] = { + .name = "EaseSineIn", + .func = EaseSineIn, + }, + [EASE_SINE_OUT] = { + .name = "EaseSineOut", + .func = EaseSineOut, + }, + [EASE_SINE_IN_OUT] = { + .name = "EaseSineInOut", + .func = EaseSineInOut, + }, + [EASE_CIRC_IN] = { + .name = "EaseCircIn", + .func = EaseCircIn, + }, + [EASE_CIRC_OUT] = { + .name = "EaseCircOut", + .func = EaseCircOut, + }, + [EASE_CIRC_IN_OUT] = { + .name = "EaseCircInOut", + .func = EaseCircInOut, + }, + [EASE_CUBIC_IN] = { + .name = "EaseCubicIn", + .func = EaseCubicIn, + }, + [EASE_CUBIC_OUT] = { + .name = "EaseCubicOut", + .func = EaseCubicOut, + }, + [EASE_CUBIC_IN_OUT] = { + .name = "EaseCubicInOut", + .func = EaseCubicInOut, + }, + [EASE_QUAD_IN] = { + .name = "EaseQuadIn", + .func = EaseQuadIn, + }, + [EASE_QUAD_OUT] = { + .name = "EaseQuadOut", + .func = EaseQuadOut, + }, + [EASE_QUAD_IN_OUT] = { + .name = "EaseQuadInOut", + .func = EaseQuadInOut, + }, + [EASE_EXPO_IN] = { + .name = "EaseExpoIn", + .func = EaseExpoIn, + }, + [EASE_EXPO_OUT] = { + .name = "EaseExpoOut", + .func = EaseExpoOut, + }, + [EASE_EXPO_IN_OUT] = { + .name = "EaseExpoInOut", + .func = EaseExpoInOut, + }, + [EASE_BACK_IN] = { + .name = "EaseBackIn", + .func = EaseBackIn, + }, + [EASE_BACK_OUT] = { + .name = "EaseBackOut", + .func = EaseBackOut, + }, + [EASE_BACK_IN_OUT] = { + .name = "EaseBackInOut", + .func = EaseBackInOut, + }, + [EASE_BOUNCE_OUT] = { + .name = "EaseBounceOut", + .func = EaseBounceOut, + }, + [EASE_BOUNCE_IN] = { + .name = "EaseBounceIn", + .func = EaseBounceIn, + }, + [EASE_BOUNCE_IN_OUT] = { + .name = "EaseBounceInOut", + .func = EaseBounceInOut, + }, + [EASE_ELASTIC_IN] = { + .name = "EaseElasticIn", + .func = EaseElasticIn, + }, + [EASE_ELASTIC_OUT] = { + .name = "EaseElasticOut", + .func = EaseElasticOut, + }, + [EASE_ELASTIC_IN_OUT] = { + .name = "EaseElasticInOut", + .func = EaseElasticInOut, + }, + [EASING_NONE] = { + .name = "None", + .func = NoEase, + }, +}; + + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + Vector2 ballPos = { .x = START_X, .y = START_Y }; + float t = 0.0f; // Current time (in any unit measure, but same unit as duration) + float d = D_DFT; // Total time it should take to complete (duration) + bool paused = true; + bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop + + enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis + enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis + + InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); + SetTargetFPS(TARGET_FPS); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) + boundedT = 1 - boundedT; + + // Choose easing for the X axis + if (IsKeyPressed(KEY_NEXT_EASE_X)) + { + ++easingX; + + if (easingX > EASING_NONE) + easingX = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_X)) + { + if (easingX == 0) + easingX = EASING_NONE; + else + --easingX; + } + + // Choose easing for the Y axis + if (IsKeyPressed(KEY_NEXT_EASE_Y)) + { + ++easingY; + + if (easingY > EASING_NONE) + easingY = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_Y)) + { + if (easingY == 0) + easingY = EASING_NONE; + else + --easingY; + } + + // Change d (duration) value + if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) + d += D_STEP; + else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) + d -= D_STEP; + + if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) + d += D_STEP_FINE; + else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) + d -= D_STEP_FINE; + + // Play, pause and restart controls + if (IsKeyPressed(KEY_RESTART) || + IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || + IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || + IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || + IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || + IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || + (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) + { + t = 0.0f; + ballPos.x = START_X; + ballPos.y = START_Y; + paused = true; + } + + if (IsKeyPressed(KEY_PLAY_PAUSE)) + paused = 1 - paused; + + // Movement computation + if ((paused == false) && + ((boundedT == true && t < d) || boundedT == false)) + { + ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); + ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); + t += T_ADVANCE; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BG_COLOR); + + // Draw information text + int line = 0; + + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), + 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw instructions text + line = 1; + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw ball + DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} + + +// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. +static float NoEase(float t, float b, float c, float d) +{ + float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) + d += burn; + + return b; +} \ No newline at end of file -- cgit v1.2.3 From d5f664373d0e28984c50fdbfd947071c0eeec7c6 Mon Sep 17 00:00:00 2001 From: flashback-fx Date: Fri, 24 May 2019 22:18:44 +0000 Subject: Move easings_example to its proper place --- examples/core/easings_example.c | 354 -------------------------------------- examples/others/easings.h | 263 ++++++++++++++++++++++++++++ examples/others/easings_example.c | 354 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+), 354 deletions(-) delete mode 100644 examples/core/easings_example.c create mode 100644 examples/others/easings.h create mode 100644 examples/others/easings_example.c (limited to 'examples') diff --git a/examples/core/easings_example.c b/examples/core/easings_example.c deleted file mode 100644 index d42b3c33..00000000 --- a/examples/core/easings_example.c +++ /dev/null @@ -1,354 +0,0 @@ -/******************************************************************************************* -* -* raylib [easings] example -* -* 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) -* -* Copyright (c) 2019 Juan Miguel López -* -********************************************************************************************/ - - -#include -#include "easings.h" - - -// Application constants -#define SCR_WIDTH 800 -#define SCR_HEIGHT 450 -#define BALL_RADIUS 16.0f -#define BALL_COLOR MAROON -#define PAD 80.0f -#define START_X (0.0f + (BALL_RADIUS) + (PAD)) -#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) -#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) -#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) -#define T_ADVANCE 1.0f -#define D_DFT 300.0f -#define TARGET_FPS 60 -#define BG_COLOR RAYWHITE -#define TEXT_COLOR LIGHTGRAY -#define FONT_SIZE 20 -#define D_STEP 20.0f -#define D_STEP_FINE 2.0f -#define D_MIN 1.0f -#define D_MAX 10000.0f - -// Application control keys -#define KEY_NEXT_EASE_X KEY_RIGHT -#define KEY_PREV_EASE_X KEY_LEFT -#define KEY_NEXT_EASE_Y KEY_DOWN -#define KEY_PREV_EASE_Y KEY_UP -#define KEY_INCR_D_STEP KEY_W -#define KEY_DECR_D_STEP KEY_Q -#define KEY_INCR_D_FINE KEY_S -#define KEY_DECR_D_FINE KEY_A -#define KEY_PLAY_PAUSE KEY_ENTER -#define KEY_RESTART KEY_SPACE -#define KEY_TOGGLE_UNBOUNDED_T KEY_T - - -// Easing types -enum EasingTypes { - EASE_LINEAR_NONE, - EASE_LINEAR_IN, - EASE_LINEAR_OUT, - EASE_LINEAR_IN_OUT, - EASE_SINE_IN, - EASE_SINE_OUT, - EASE_SINE_IN_OUT, - EASE_CIRC_IN, - EASE_CIRC_OUT, - EASE_CIRC_IN_OUT, - EASE_CUBIC_IN, - EASE_CUBIC_OUT, - EASE_CUBIC_IN_OUT, - EASE_QUAD_IN, - EASE_QUAD_OUT, - EASE_QUAD_IN_OUT, - EASE_EXPO_IN, - EASE_EXPO_OUT, - EASE_EXPO_IN_OUT, - EASE_BACK_IN, - EASE_BACK_OUT, - EASE_BACK_IN_OUT, - EASE_BOUNCE_OUT, - EASE_BOUNCE_IN, - EASE_BOUNCE_IN_OUT, - EASE_ELASTIC_IN, - EASE_ELASTIC_OUT, - EASE_ELASTIC_IN_OUT, - NUM_EASING_TYPES, - EASING_NONE = NUM_EASING_TYPES -}; - - -static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis - - -// Easing functions reference data -static const struct { - const char *name; - float (*func)(float, float, float, float); -} Easings[] = { - [EASE_LINEAR_NONE] = { - .name = "EaseLinearNone", - .func = EaseLinearNone, - }, - [EASE_LINEAR_IN] = { - .name = "EaseLinearIn", - .func = EaseLinearIn, - }, - [EASE_LINEAR_OUT] = { - .name = "EaseLinearOut", - .func = EaseLinearOut, - }, - [EASE_LINEAR_IN_OUT] = { - .name = "EaseLinearInOut", - .func = EaseLinearInOut, - }, - [EASE_SINE_IN] = { - .name = "EaseSineIn", - .func = EaseSineIn, - }, - [EASE_SINE_OUT] = { - .name = "EaseSineOut", - .func = EaseSineOut, - }, - [EASE_SINE_IN_OUT] = { - .name = "EaseSineInOut", - .func = EaseSineInOut, - }, - [EASE_CIRC_IN] = { - .name = "EaseCircIn", - .func = EaseCircIn, - }, - [EASE_CIRC_OUT] = { - .name = "EaseCircOut", - .func = EaseCircOut, - }, - [EASE_CIRC_IN_OUT] = { - .name = "EaseCircInOut", - .func = EaseCircInOut, - }, - [EASE_CUBIC_IN] = { - .name = "EaseCubicIn", - .func = EaseCubicIn, - }, - [EASE_CUBIC_OUT] = { - .name = "EaseCubicOut", - .func = EaseCubicOut, - }, - [EASE_CUBIC_IN_OUT] = { - .name = "EaseCubicInOut", - .func = EaseCubicInOut, - }, - [EASE_QUAD_IN] = { - .name = "EaseQuadIn", - .func = EaseQuadIn, - }, - [EASE_QUAD_OUT] = { - .name = "EaseQuadOut", - .func = EaseQuadOut, - }, - [EASE_QUAD_IN_OUT] = { - .name = "EaseQuadInOut", - .func = EaseQuadInOut, - }, - [EASE_EXPO_IN] = { - .name = "EaseExpoIn", - .func = EaseExpoIn, - }, - [EASE_EXPO_OUT] = { - .name = "EaseExpoOut", - .func = EaseExpoOut, - }, - [EASE_EXPO_IN_OUT] = { - .name = "EaseExpoInOut", - .func = EaseExpoInOut, - }, - [EASE_BACK_IN] = { - .name = "EaseBackIn", - .func = EaseBackIn, - }, - [EASE_BACK_OUT] = { - .name = "EaseBackOut", - .func = EaseBackOut, - }, - [EASE_BACK_IN_OUT] = { - .name = "EaseBackInOut", - .func = EaseBackInOut, - }, - [EASE_BOUNCE_OUT] = { - .name = "EaseBounceOut", - .func = EaseBounceOut, - }, - [EASE_BOUNCE_IN] = { - .name = "EaseBounceIn", - .func = EaseBounceIn, - }, - [EASE_BOUNCE_IN_OUT] = { - .name = "EaseBounceInOut", - .func = EaseBounceInOut, - }, - [EASE_ELASTIC_IN] = { - .name = "EaseElasticIn", - .func = EaseElasticIn, - }, - [EASE_ELASTIC_OUT] = { - .name = "EaseElasticOut", - .func = EaseElasticOut, - }, - [EASE_ELASTIC_IN_OUT] = { - .name = "EaseElasticInOut", - .func = EaseElasticInOut, - }, - [EASING_NONE] = { - .name = "None", - .func = NoEase, - }, -}; - - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - Vector2 ballPos = { .x = START_X, .y = START_Y }; - float t = 0.0f; // Current time (in any unit measure, but same unit as duration) - float d = D_DFT; // Total time it should take to complete (duration) - bool paused = true; - bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop - - enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis - enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis - - InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); - SetTargetFPS(TARGET_FPS); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) - boundedT = 1 - boundedT; - - // Choose easing for the X axis - if (IsKeyPressed(KEY_NEXT_EASE_X)) - { - ++easingX; - - if (easingX > EASING_NONE) - easingX = 0; - } - else if (IsKeyPressed(KEY_PREV_EASE_X)) - { - if (easingX == 0) - easingX = EASING_NONE; - else - --easingX; - } - - // Choose easing for the Y axis - if (IsKeyPressed(KEY_NEXT_EASE_Y)) - { - ++easingY; - - if (easingY > EASING_NONE) - easingY = 0; - } - else if (IsKeyPressed(KEY_PREV_EASE_Y)) - { - if (easingY == 0) - easingY = EASING_NONE; - else - --easingY; - } - - // Change d (duration) value - if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) - d += D_STEP; - else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) - d -= D_STEP; - - if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) - d += D_STEP_FINE; - else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) - d -= D_STEP_FINE; - - // Play, pause and restart controls - if (IsKeyPressed(KEY_RESTART) || - IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || - IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || - IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || - IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || - IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || - (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) - { - t = 0.0f; - ballPos.x = START_X; - ballPos.y = START_Y; - paused = true; - } - - if (IsKeyPressed(KEY_PLAY_PAUSE)) - paused = 1 - paused; - - // Movement computation - if ((paused == false) && - ((boundedT == true && t < d) || boundedT == false)) - { - ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); - ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); - t += T_ADVANCE; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(BG_COLOR); - - // Draw information text - int line = 0; - - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), - 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - - // Draw instructions text - line = 1; - DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - - // Draw ball - DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; -} - - -// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. -static float NoEase(float t, float b, float c, float d) -{ - float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) - d += burn; - - return b; -} \ No newline at end of file diff --git a/examples/others/easings.h b/examples/others/easings.h new file mode 100644 index 00000000..c7ee5649 --- /dev/null +++ b/examples/others/easings.h @@ -0,0 +1,263 @@ +/******************************************************************************************* +* +* raylib easings (header only file) +* +* Useful easing functions for values animation +* +* This header uses: +* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster. +* // This requires lots of memory on system. +* How to use: +* The four inputs t,b,c,d are defined as follows: +* t = current time (in any unit measure, but same unit as duration) +* b = starting value to interpolate +* c = the total change in value of b that needs to occur +* d = total time it should take to complete (duration) +* +* Example: +* +* int currentTime = 0; +* int duration = 100; +* float startPositionX = 0.0f; +* float finalPositionX = 30.0f; +* float currentPositionX = startPositionX; +* +* while (currentPositionX < finalPositionX) +* { +* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration); +* currentTime++; +* } +* +* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/) +* +* Robert Penner License +* --------------------------------------------------------------------------------- +* Open source under the BSD License. +* +* Copyright (c) 2001 Robert Penner. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* - Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* - Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* - Neither the name of the author nor the names of contributors may be used +* to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* --------------------------------------------------------------------------------- +* +* Copyright (c) 2015 Ramon Santamaria +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef EASINGS_H +#define EASINGS_H + +#define EASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline + +#if defined(EASINGS_STATIC_INLINE) + #define EASEDEF static inline +#else + #define EASEDEF extern +#endif + +#include // Required for: sin(), cos(), sqrt(), pow() + +#ifndef PI + #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h +#endif + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +// Linear Easing functions +EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); } + +// Sine Easing functions +EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2.0f)) + c + b); } +EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2.0f)) + b); } +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cos(PI*t/d) - 1.0f) + b); } + +// Circular Easing functions +EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrt(1.0f - t*t) - 1.0f) + b); } +EASEDEF float EaseCircOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*sqrt(1.0f - t*t) + b); } +EASEDEF float EaseCircInOut(float t, float b, float c, float d) +{ + if ((t/=d/2.0f) < 1.0f) return (-c/2.0f*(sqrt(1.0f - t*t) - 1.0f) + b); + t -= 2.0f; return (c/2.0f*(sqrt(1.0f - t*t) + 1.0f) + b); +} + +// Cubic Easing functions +EASEDEF float EaseCubicIn(float t, float b, float c, float d) { t /= d; return (c*t*t*t + b); } +EASEDEF float EaseCubicOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*(t*t*t + 1.0f) + b); } +EASEDEF float EaseCubicInOut(float t, float b, float c, float d) +{ + if ((t/=d/2.0f) < 1.0f) return (c/2.0f*t*t*t + b); + t -= 2.0f; return (c/2.0f*(t*t*t + 2.0f) + b); +} + +// Quadratic Easing functions +EASEDEF float EaseQuadIn(float t, float b, float c, float d) { t /= d; return (c*t*t + b); } +EASEDEF float EaseQuadOut(float t, float b, float c, float d) { t /= d; return (-c*t*(t - 2.0f) + b); } +EASEDEF float EaseQuadInOut(float t, float b, float c, float d) +{ + if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b); + return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b); +} + +// Exponential Easing functions +EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0.0f) ? b : (c*pow(2.0f, 10.0f*(t/d - 1.0f)) + b); } +EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-pow(2.0f, -10.0f*t/d) + 1.0f) + b); } +EASEDEF float EaseExpoInOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if (t == d) return (b + c); + if ((t/=d/2.0f) < 1.0f) return (c/2.0f*pow(2.0f, 10.0f*(t - 1.0f)) + b); + + return (c/2.0f*(-pow(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b); +} + +// Back Easing functions +EASEDEF float EaseBackIn(float t, float b, float c, float d) +{ + float s = 1.70158f; + float postFix = t/=d; + return (c*(postFix)*t*((s + 1.0f)*t - s) + b); +} + +EASEDEF float EaseBackOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + t = t/d - 1.0f; + return (c*(t*t*((s + 1.0f)*t + s) + 1.0f) + b); +} + +EASEDEF float EaseBackInOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + if ((t/=d/2.0f) < 1.0f) + { + s *= 1.525f; + return (c/2.0f*(t*t*((s + 1.0f)*t - s)) + b); + } + + float postFix = t-=2.0f; + s *= 1.525f; + return (c/2.0f*((postFix)*t*((s + 1.0f)*t + s) + 2.0f) + b); +} + +// Bounce Easing functions +EASEDEF float EaseBounceOut(float t, float b, float c, float d) +{ + if ((t/=d) < (1.0f/2.75f)) + { + return (c*(7.5625f*t*t) + b); + } + else if (t < (2.0f/2.75f)) + { + float postFix = t-=(1.5f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.75f) + b); + } + else if (t < (2.5/2.75)) + { + float postFix = t-=(2.25f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.9375f) + b); + } + else + { + float postFix = t-=(2.625f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.984375f) + b); + } +} + +EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d - t, 0.0f, c, d) + b); } +EASEDEF float EaseBounceInOut(float t, float b, float c, float d) +{ + if (t < d/2.0f) return (EaseBounceIn(t*2.0f, 0.0f, c, d)*0.5f + b); + else return (EaseBounceOut(t*2.0f - d, 0.0f, c, d)*0.5f + c*0.5f + b); +} + +// Elastic Easing functions +EASEDEF float EaseElasticIn(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d) == 1.0f) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4.0f; + float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); + + return (-(postFix*sin((t*d-s)*(2.0f*PI)/p )) + b); +} + +EASEDEF float EaseElasticOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d) == 1.0f) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4.0f; + + return (a*pow(2.0f,-10.0f*t)*sin((t*d-s)*(2.0f*PI)/p) + c + b); +} + +EASEDEF float EaseElasticInOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d/2.0f) == 2.0f) return (b + c); + + float p = d*(0.3f*1.5f); + float a = c; + float s = p/4.0f; + + if (t < 1.0f) + { + float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); + return -0.5f*(postFix*sin((t*d-s)*(2.0f*PI)/p)) + b; + } + + float postFix = a*pow(2.0f, -10.0f*(t-=1.0f)); + + return (postFix*sin((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); +} + +#ifdef __cplusplus +} +#endif + +#endif // EASINGS_H diff --git a/examples/others/easings_example.c b/examples/others/easings_example.c new file mode 100644 index 00000000..d42b3c33 --- /dev/null +++ b/examples/others/easings_example.c @@ -0,0 +1,354 @@ +/******************************************************************************************* +* +* raylib [easings] example +* +* 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) +* +* Copyright (c) 2019 Juan Miguel López +* +********************************************************************************************/ + + +#include +#include "easings.h" + + +// Application constants +#define SCR_WIDTH 800 +#define SCR_HEIGHT 450 +#define BALL_RADIUS 16.0f +#define BALL_COLOR MAROON +#define PAD 80.0f +#define START_X (0.0f + (BALL_RADIUS) + (PAD)) +#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) +#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) +#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) +#define T_ADVANCE 1.0f +#define D_DFT 300.0f +#define TARGET_FPS 60 +#define BG_COLOR RAYWHITE +#define TEXT_COLOR LIGHTGRAY +#define FONT_SIZE 20 +#define D_STEP 20.0f +#define D_STEP_FINE 2.0f +#define D_MIN 1.0f +#define D_MAX 10000.0f + +// Application control keys +#define KEY_NEXT_EASE_X KEY_RIGHT +#define KEY_PREV_EASE_X KEY_LEFT +#define KEY_NEXT_EASE_Y KEY_DOWN +#define KEY_PREV_EASE_Y KEY_UP +#define KEY_INCR_D_STEP KEY_W +#define KEY_DECR_D_STEP KEY_Q +#define KEY_INCR_D_FINE KEY_S +#define KEY_DECR_D_FINE KEY_A +#define KEY_PLAY_PAUSE KEY_ENTER +#define KEY_RESTART KEY_SPACE +#define KEY_TOGGLE_UNBOUNDED_T KEY_T + + +// Easing types +enum EasingTypes { + EASE_LINEAR_NONE, + EASE_LINEAR_IN, + EASE_LINEAR_OUT, + EASE_LINEAR_IN_OUT, + EASE_SINE_IN, + EASE_SINE_OUT, + EASE_SINE_IN_OUT, + EASE_CIRC_IN, + EASE_CIRC_OUT, + EASE_CIRC_IN_OUT, + EASE_CUBIC_IN, + EASE_CUBIC_OUT, + EASE_CUBIC_IN_OUT, + EASE_QUAD_IN, + EASE_QUAD_OUT, + EASE_QUAD_IN_OUT, + EASE_EXPO_IN, + EASE_EXPO_OUT, + EASE_EXPO_IN_OUT, + EASE_BACK_IN, + EASE_BACK_OUT, + EASE_BACK_IN_OUT, + EASE_BOUNCE_OUT, + EASE_BOUNCE_IN, + EASE_BOUNCE_IN_OUT, + EASE_ELASTIC_IN, + EASE_ELASTIC_OUT, + EASE_ELASTIC_IN_OUT, + NUM_EASING_TYPES, + EASING_NONE = NUM_EASING_TYPES +}; + + +static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis + + +// Easing functions reference data +static const struct { + const char *name; + float (*func)(float, float, float, float); +} Easings[] = { + [EASE_LINEAR_NONE] = { + .name = "EaseLinearNone", + .func = EaseLinearNone, + }, + [EASE_LINEAR_IN] = { + .name = "EaseLinearIn", + .func = EaseLinearIn, + }, + [EASE_LINEAR_OUT] = { + .name = "EaseLinearOut", + .func = EaseLinearOut, + }, + [EASE_LINEAR_IN_OUT] = { + .name = "EaseLinearInOut", + .func = EaseLinearInOut, + }, + [EASE_SINE_IN] = { + .name = "EaseSineIn", + .func = EaseSineIn, + }, + [EASE_SINE_OUT] = { + .name = "EaseSineOut", + .func = EaseSineOut, + }, + [EASE_SINE_IN_OUT] = { + .name = "EaseSineInOut", + .func = EaseSineInOut, + }, + [EASE_CIRC_IN] = { + .name = "EaseCircIn", + .func = EaseCircIn, + }, + [EASE_CIRC_OUT] = { + .name = "EaseCircOut", + .func = EaseCircOut, + }, + [EASE_CIRC_IN_OUT] = { + .name = "EaseCircInOut", + .func = EaseCircInOut, + }, + [EASE_CUBIC_IN] = { + .name = "EaseCubicIn", + .func = EaseCubicIn, + }, + [EASE_CUBIC_OUT] = { + .name = "EaseCubicOut", + .func = EaseCubicOut, + }, + [EASE_CUBIC_IN_OUT] = { + .name = "EaseCubicInOut", + .func = EaseCubicInOut, + }, + [EASE_QUAD_IN] = { + .name = "EaseQuadIn", + .func = EaseQuadIn, + }, + [EASE_QUAD_OUT] = { + .name = "EaseQuadOut", + .func = EaseQuadOut, + }, + [EASE_QUAD_IN_OUT] = { + .name = "EaseQuadInOut", + .func = EaseQuadInOut, + }, + [EASE_EXPO_IN] = { + .name = "EaseExpoIn", + .func = EaseExpoIn, + }, + [EASE_EXPO_OUT] = { + .name = "EaseExpoOut", + .func = EaseExpoOut, + }, + [EASE_EXPO_IN_OUT] = { + .name = "EaseExpoInOut", + .func = EaseExpoInOut, + }, + [EASE_BACK_IN] = { + .name = "EaseBackIn", + .func = EaseBackIn, + }, + [EASE_BACK_OUT] = { + .name = "EaseBackOut", + .func = EaseBackOut, + }, + [EASE_BACK_IN_OUT] = { + .name = "EaseBackInOut", + .func = EaseBackInOut, + }, + [EASE_BOUNCE_OUT] = { + .name = "EaseBounceOut", + .func = EaseBounceOut, + }, + [EASE_BOUNCE_IN] = { + .name = "EaseBounceIn", + .func = EaseBounceIn, + }, + [EASE_BOUNCE_IN_OUT] = { + .name = "EaseBounceInOut", + .func = EaseBounceInOut, + }, + [EASE_ELASTIC_IN] = { + .name = "EaseElasticIn", + .func = EaseElasticIn, + }, + [EASE_ELASTIC_OUT] = { + .name = "EaseElasticOut", + .func = EaseElasticOut, + }, + [EASE_ELASTIC_IN_OUT] = { + .name = "EaseElasticInOut", + .func = EaseElasticInOut, + }, + [EASING_NONE] = { + .name = "None", + .func = NoEase, + }, +}; + + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + Vector2 ballPos = { .x = START_X, .y = START_Y }; + float t = 0.0f; // Current time (in any unit measure, but same unit as duration) + float d = D_DFT; // Total time it should take to complete (duration) + bool paused = true; + bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop + + enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis + enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis + + InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); + SetTargetFPS(TARGET_FPS); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) + boundedT = 1 - boundedT; + + // Choose easing for the X axis + if (IsKeyPressed(KEY_NEXT_EASE_X)) + { + ++easingX; + + if (easingX > EASING_NONE) + easingX = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_X)) + { + if (easingX == 0) + easingX = EASING_NONE; + else + --easingX; + } + + // Choose easing for the Y axis + if (IsKeyPressed(KEY_NEXT_EASE_Y)) + { + ++easingY; + + if (easingY > EASING_NONE) + easingY = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_Y)) + { + if (easingY == 0) + easingY = EASING_NONE; + else + --easingY; + } + + // Change d (duration) value + if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) + d += D_STEP; + else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) + d -= D_STEP; + + if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) + d += D_STEP_FINE; + else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) + d -= D_STEP_FINE; + + // Play, pause and restart controls + if (IsKeyPressed(KEY_RESTART) || + IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || + IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || + IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || + IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || + IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || + (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) + { + t = 0.0f; + ballPos.x = START_X; + ballPos.y = START_Y; + paused = true; + } + + if (IsKeyPressed(KEY_PLAY_PAUSE)) + paused = 1 - paused; + + // Movement computation + if ((paused == false) && + ((boundedT == true && t < d) || boundedT == false)) + { + ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); + ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); + t += T_ADVANCE; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BG_COLOR); + + // Draw information text + int line = 0; + + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), + 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw instructions text + line = 1; + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw ball + DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} + + +// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. +static float NoEase(float t, float b, float c, float d) +{ + float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) + d += burn; + + return b; +} \ No newline at end of file -- cgit v1.2.3 From bc285f71ed98a16fc6082dfeb25293ca84c936c9 Mon Sep 17 00:00:00 2001 From: flashback-fx Date: Fri, 24 May 2019 22:24:01 +0000 Subject: rename easings_example.c to easings_testbed.c --- examples/others/easings_example.c | 354 -------------------------------------- examples/others/easings_testbed.c | 354 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+), 354 deletions(-) delete mode 100644 examples/others/easings_example.c create mode 100644 examples/others/easings_testbed.c (limited to 'examples') diff --git a/examples/others/easings_example.c b/examples/others/easings_example.c deleted file mode 100644 index d42b3c33..00000000 --- a/examples/others/easings_example.c +++ /dev/null @@ -1,354 +0,0 @@ -/******************************************************************************************* -* -* raylib [easings] example -* -* 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) -* -* Copyright (c) 2019 Juan Miguel López -* -********************************************************************************************/ - - -#include -#include "easings.h" - - -// Application constants -#define SCR_WIDTH 800 -#define SCR_HEIGHT 450 -#define BALL_RADIUS 16.0f -#define BALL_COLOR MAROON -#define PAD 80.0f -#define START_X (0.0f + (BALL_RADIUS) + (PAD)) -#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) -#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) -#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) -#define T_ADVANCE 1.0f -#define D_DFT 300.0f -#define TARGET_FPS 60 -#define BG_COLOR RAYWHITE -#define TEXT_COLOR LIGHTGRAY -#define FONT_SIZE 20 -#define D_STEP 20.0f -#define D_STEP_FINE 2.0f -#define D_MIN 1.0f -#define D_MAX 10000.0f - -// Application control keys -#define KEY_NEXT_EASE_X KEY_RIGHT -#define KEY_PREV_EASE_X KEY_LEFT -#define KEY_NEXT_EASE_Y KEY_DOWN -#define KEY_PREV_EASE_Y KEY_UP -#define KEY_INCR_D_STEP KEY_W -#define KEY_DECR_D_STEP KEY_Q -#define KEY_INCR_D_FINE KEY_S -#define KEY_DECR_D_FINE KEY_A -#define KEY_PLAY_PAUSE KEY_ENTER -#define KEY_RESTART KEY_SPACE -#define KEY_TOGGLE_UNBOUNDED_T KEY_T - - -// Easing types -enum EasingTypes { - EASE_LINEAR_NONE, - EASE_LINEAR_IN, - EASE_LINEAR_OUT, - EASE_LINEAR_IN_OUT, - EASE_SINE_IN, - EASE_SINE_OUT, - EASE_SINE_IN_OUT, - EASE_CIRC_IN, - EASE_CIRC_OUT, - EASE_CIRC_IN_OUT, - EASE_CUBIC_IN, - EASE_CUBIC_OUT, - EASE_CUBIC_IN_OUT, - EASE_QUAD_IN, - EASE_QUAD_OUT, - EASE_QUAD_IN_OUT, - EASE_EXPO_IN, - EASE_EXPO_OUT, - EASE_EXPO_IN_OUT, - EASE_BACK_IN, - EASE_BACK_OUT, - EASE_BACK_IN_OUT, - EASE_BOUNCE_OUT, - EASE_BOUNCE_IN, - EASE_BOUNCE_IN_OUT, - EASE_ELASTIC_IN, - EASE_ELASTIC_OUT, - EASE_ELASTIC_IN_OUT, - NUM_EASING_TYPES, - EASING_NONE = NUM_EASING_TYPES -}; - - -static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis - - -// Easing functions reference data -static const struct { - const char *name; - float (*func)(float, float, float, float); -} Easings[] = { - [EASE_LINEAR_NONE] = { - .name = "EaseLinearNone", - .func = EaseLinearNone, - }, - [EASE_LINEAR_IN] = { - .name = "EaseLinearIn", - .func = EaseLinearIn, - }, - [EASE_LINEAR_OUT] = { - .name = "EaseLinearOut", - .func = EaseLinearOut, - }, - [EASE_LINEAR_IN_OUT] = { - .name = "EaseLinearInOut", - .func = EaseLinearInOut, - }, - [EASE_SINE_IN] = { - .name = "EaseSineIn", - .func = EaseSineIn, - }, - [EASE_SINE_OUT] = { - .name = "EaseSineOut", - .func = EaseSineOut, - }, - [EASE_SINE_IN_OUT] = { - .name = "EaseSineInOut", - .func = EaseSineInOut, - }, - [EASE_CIRC_IN] = { - .name = "EaseCircIn", - .func = EaseCircIn, - }, - [EASE_CIRC_OUT] = { - .name = "EaseCircOut", - .func = EaseCircOut, - }, - [EASE_CIRC_IN_OUT] = { - .name = "EaseCircInOut", - .func = EaseCircInOut, - }, - [EASE_CUBIC_IN] = { - .name = "EaseCubicIn", - .func = EaseCubicIn, - }, - [EASE_CUBIC_OUT] = { - .name = "EaseCubicOut", - .func = EaseCubicOut, - }, - [EASE_CUBIC_IN_OUT] = { - .name = "EaseCubicInOut", - .func = EaseCubicInOut, - }, - [EASE_QUAD_IN] = { - .name = "EaseQuadIn", - .func = EaseQuadIn, - }, - [EASE_QUAD_OUT] = { - .name = "EaseQuadOut", - .func = EaseQuadOut, - }, - [EASE_QUAD_IN_OUT] = { - .name = "EaseQuadInOut", - .func = EaseQuadInOut, - }, - [EASE_EXPO_IN] = { - .name = "EaseExpoIn", - .func = EaseExpoIn, - }, - [EASE_EXPO_OUT] = { - .name = "EaseExpoOut", - .func = EaseExpoOut, - }, - [EASE_EXPO_IN_OUT] = { - .name = "EaseExpoInOut", - .func = EaseExpoInOut, - }, - [EASE_BACK_IN] = { - .name = "EaseBackIn", - .func = EaseBackIn, - }, - [EASE_BACK_OUT] = { - .name = "EaseBackOut", - .func = EaseBackOut, - }, - [EASE_BACK_IN_OUT] = { - .name = "EaseBackInOut", - .func = EaseBackInOut, - }, - [EASE_BOUNCE_OUT] = { - .name = "EaseBounceOut", - .func = EaseBounceOut, - }, - [EASE_BOUNCE_IN] = { - .name = "EaseBounceIn", - .func = EaseBounceIn, - }, - [EASE_BOUNCE_IN_OUT] = { - .name = "EaseBounceInOut", - .func = EaseBounceInOut, - }, - [EASE_ELASTIC_IN] = { - .name = "EaseElasticIn", - .func = EaseElasticIn, - }, - [EASE_ELASTIC_OUT] = { - .name = "EaseElasticOut", - .func = EaseElasticOut, - }, - [EASE_ELASTIC_IN_OUT] = { - .name = "EaseElasticInOut", - .func = EaseElasticInOut, - }, - [EASING_NONE] = { - .name = "None", - .func = NoEase, - }, -}; - - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - Vector2 ballPos = { .x = START_X, .y = START_Y }; - float t = 0.0f; // Current time (in any unit measure, but same unit as duration) - float d = D_DFT; // Total time it should take to complete (duration) - bool paused = true; - bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop - - enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis - enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis - - InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); - SetTargetFPS(TARGET_FPS); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) - boundedT = 1 - boundedT; - - // Choose easing for the X axis - if (IsKeyPressed(KEY_NEXT_EASE_X)) - { - ++easingX; - - if (easingX > EASING_NONE) - easingX = 0; - } - else if (IsKeyPressed(KEY_PREV_EASE_X)) - { - if (easingX == 0) - easingX = EASING_NONE; - else - --easingX; - } - - // Choose easing for the Y axis - if (IsKeyPressed(KEY_NEXT_EASE_Y)) - { - ++easingY; - - if (easingY > EASING_NONE) - easingY = 0; - } - else if (IsKeyPressed(KEY_PREV_EASE_Y)) - { - if (easingY == 0) - easingY = EASING_NONE; - else - --easingY; - } - - // Change d (duration) value - if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) - d += D_STEP; - else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) - d -= D_STEP; - - if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) - d += D_STEP_FINE; - else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) - d -= D_STEP_FINE; - - // Play, pause and restart controls - if (IsKeyPressed(KEY_RESTART) || - IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || - IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || - IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || - IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || - IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || - (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) - { - t = 0.0f; - ballPos.x = START_X; - ballPos.y = START_Y; - paused = true; - } - - if (IsKeyPressed(KEY_PLAY_PAUSE)) - paused = 1 - paused; - - // Movement computation - if ((paused == false) && - ((boundedT == true && t < d) || boundedT == false)) - { - ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); - ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); - t += T_ADVANCE; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(BG_COLOR); - - // Draw information text - int line = 0; - - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), - 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - - // Draw instructions text - line = 1; - DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - - // Draw ball - DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; -} - - -// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. -static float NoEase(float t, float b, float c, float d) -{ - float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) - d += burn; - - return b; -} \ No newline at end of file diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c new file mode 100644 index 00000000..d42b3c33 --- /dev/null +++ b/examples/others/easings_testbed.c @@ -0,0 +1,354 @@ +/******************************************************************************************* +* +* raylib [easings] example +* +* 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) +* +* Copyright (c) 2019 Juan Miguel López +* +********************************************************************************************/ + + +#include +#include "easings.h" + + +// Application constants +#define SCR_WIDTH 800 +#define SCR_HEIGHT 450 +#define BALL_RADIUS 16.0f +#define BALL_COLOR MAROON +#define PAD 80.0f +#define START_X (0.0f + (BALL_RADIUS) + (PAD)) +#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) +#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) +#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) +#define T_ADVANCE 1.0f +#define D_DFT 300.0f +#define TARGET_FPS 60 +#define BG_COLOR RAYWHITE +#define TEXT_COLOR LIGHTGRAY +#define FONT_SIZE 20 +#define D_STEP 20.0f +#define D_STEP_FINE 2.0f +#define D_MIN 1.0f +#define D_MAX 10000.0f + +// Application control keys +#define KEY_NEXT_EASE_X KEY_RIGHT +#define KEY_PREV_EASE_X KEY_LEFT +#define KEY_NEXT_EASE_Y KEY_DOWN +#define KEY_PREV_EASE_Y KEY_UP +#define KEY_INCR_D_STEP KEY_W +#define KEY_DECR_D_STEP KEY_Q +#define KEY_INCR_D_FINE KEY_S +#define KEY_DECR_D_FINE KEY_A +#define KEY_PLAY_PAUSE KEY_ENTER +#define KEY_RESTART KEY_SPACE +#define KEY_TOGGLE_UNBOUNDED_T KEY_T + + +// Easing types +enum EasingTypes { + EASE_LINEAR_NONE, + EASE_LINEAR_IN, + EASE_LINEAR_OUT, + EASE_LINEAR_IN_OUT, + EASE_SINE_IN, + EASE_SINE_OUT, + EASE_SINE_IN_OUT, + EASE_CIRC_IN, + EASE_CIRC_OUT, + EASE_CIRC_IN_OUT, + EASE_CUBIC_IN, + EASE_CUBIC_OUT, + EASE_CUBIC_IN_OUT, + EASE_QUAD_IN, + EASE_QUAD_OUT, + EASE_QUAD_IN_OUT, + EASE_EXPO_IN, + EASE_EXPO_OUT, + EASE_EXPO_IN_OUT, + EASE_BACK_IN, + EASE_BACK_OUT, + EASE_BACK_IN_OUT, + EASE_BOUNCE_OUT, + EASE_BOUNCE_IN, + EASE_BOUNCE_IN_OUT, + EASE_ELASTIC_IN, + EASE_ELASTIC_OUT, + EASE_ELASTIC_IN_OUT, + NUM_EASING_TYPES, + EASING_NONE = NUM_EASING_TYPES +}; + + +static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis + + +// Easing functions reference data +static const struct { + const char *name; + float (*func)(float, float, float, float); +} Easings[] = { + [EASE_LINEAR_NONE] = { + .name = "EaseLinearNone", + .func = EaseLinearNone, + }, + [EASE_LINEAR_IN] = { + .name = "EaseLinearIn", + .func = EaseLinearIn, + }, + [EASE_LINEAR_OUT] = { + .name = "EaseLinearOut", + .func = EaseLinearOut, + }, + [EASE_LINEAR_IN_OUT] = { + .name = "EaseLinearInOut", + .func = EaseLinearInOut, + }, + [EASE_SINE_IN] = { + .name = "EaseSineIn", + .func = EaseSineIn, + }, + [EASE_SINE_OUT] = { + .name = "EaseSineOut", + .func = EaseSineOut, + }, + [EASE_SINE_IN_OUT] = { + .name = "EaseSineInOut", + .func = EaseSineInOut, + }, + [EASE_CIRC_IN] = { + .name = "EaseCircIn", + .func = EaseCircIn, + }, + [EASE_CIRC_OUT] = { + .name = "EaseCircOut", + .func = EaseCircOut, + }, + [EASE_CIRC_IN_OUT] = { + .name = "EaseCircInOut", + .func = EaseCircInOut, + }, + [EASE_CUBIC_IN] = { + .name = "EaseCubicIn", + .func = EaseCubicIn, + }, + [EASE_CUBIC_OUT] = { + .name = "EaseCubicOut", + .func = EaseCubicOut, + }, + [EASE_CUBIC_IN_OUT] = { + .name = "EaseCubicInOut", + .func = EaseCubicInOut, + }, + [EASE_QUAD_IN] = { + .name = "EaseQuadIn", + .func = EaseQuadIn, + }, + [EASE_QUAD_OUT] = { + .name = "EaseQuadOut", + .func = EaseQuadOut, + }, + [EASE_QUAD_IN_OUT] = { + .name = "EaseQuadInOut", + .func = EaseQuadInOut, + }, + [EASE_EXPO_IN] = { + .name = "EaseExpoIn", + .func = EaseExpoIn, + }, + [EASE_EXPO_OUT] = { + .name = "EaseExpoOut", + .func = EaseExpoOut, + }, + [EASE_EXPO_IN_OUT] = { + .name = "EaseExpoInOut", + .func = EaseExpoInOut, + }, + [EASE_BACK_IN] = { + .name = "EaseBackIn", + .func = EaseBackIn, + }, + [EASE_BACK_OUT] = { + .name = "EaseBackOut", + .func = EaseBackOut, + }, + [EASE_BACK_IN_OUT] = { + .name = "EaseBackInOut", + .func = EaseBackInOut, + }, + [EASE_BOUNCE_OUT] = { + .name = "EaseBounceOut", + .func = EaseBounceOut, + }, + [EASE_BOUNCE_IN] = { + .name = "EaseBounceIn", + .func = EaseBounceIn, + }, + [EASE_BOUNCE_IN_OUT] = { + .name = "EaseBounceInOut", + .func = EaseBounceInOut, + }, + [EASE_ELASTIC_IN] = { + .name = "EaseElasticIn", + .func = EaseElasticIn, + }, + [EASE_ELASTIC_OUT] = { + .name = "EaseElasticOut", + .func = EaseElasticOut, + }, + [EASE_ELASTIC_IN_OUT] = { + .name = "EaseElasticInOut", + .func = EaseElasticInOut, + }, + [EASING_NONE] = { + .name = "None", + .func = NoEase, + }, +}; + + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + Vector2 ballPos = { .x = START_X, .y = START_Y }; + float t = 0.0f; // Current time (in any unit measure, but same unit as duration) + float d = D_DFT; // Total time it should take to complete (duration) + bool paused = true; + bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop + + enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis + enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis + + InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); + SetTargetFPS(TARGET_FPS); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) + boundedT = 1 - boundedT; + + // Choose easing for the X axis + if (IsKeyPressed(KEY_NEXT_EASE_X)) + { + ++easingX; + + if (easingX > EASING_NONE) + easingX = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_X)) + { + if (easingX == 0) + easingX = EASING_NONE; + else + --easingX; + } + + // Choose easing for the Y axis + if (IsKeyPressed(KEY_NEXT_EASE_Y)) + { + ++easingY; + + if (easingY > EASING_NONE) + easingY = 0; + } + else if (IsKeyPressed(KEY_PREV_EASE_Y)) + { + if (easingY == 0) + easingY = EASING_NONE; + else + --easingY; + } + + // Change d (duration) value + if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) + d += D_STEP; + else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) + d -= D_STEP; + + if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) + d += D_STEP_FINE; + else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) + d -= D_STEP_FINE; + + // Play, pause and restart controls + if (IsKeyPressed(KEY_RESTART) || + IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || + IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || + IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || + IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || + IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || + (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) + { + t = 0.0f; + ballPos.x = START_X; + ballPos.y = START_Y; + paused = true; + } + + if (IsKeyPressed(KEY_PLAY_PAUSE)) + paused = 1 - paused; + + // Movement computation + if ((paused == false) && + ((boundedT == true && t < d) || boundedT == false)) + { + ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); + ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); + t += T_ADVANCE; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BG_COLOR); + + // Draw information text + int line = 0; + + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), + 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw instructions text + line = 1; + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + + // Draw ball + DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} + + +// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. +static float NoEase(float t, float b, float c, float d) +{ + float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) + d += burn; + + return b; +} \ No newline at end of file -- cgit v1.2.3 From 241c4c8d14225bdf30c168802d4b16b59d5043e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 25 May 2019 01:33:03 +0200 Subject: Review easings PR --- examples/others/easings.h | 16 +- examples/others/easings_testbed.c | 311 +++++++++++--------------------------- src/easings.h | 16 +- 3 files changed, 108 insertions(+), 235 deletions(-) (limited to 'examples') diff --git a/examples/others/easings.h b/examples/others/easings.h index c7ee5649..1b08af0a 100644 --- a/examples/others/easings.h +++ b/examples/others/easings.h @@ -90,7 +90,7 @@ #define EASEDEF extern #endif -#include // Required for: sin(), cos(), sqrt(), pow() +#include // Required for: sinf(), cosf(), sqrt(), pow() #ifndef PI #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h @@ -107,9 +107,9 @@ EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); } // Sine Easing functions -EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2.0f)) + c + b); } -EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2.0f)) + b); } -EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cos(PI*t/d) - 1.0f) + b); } +EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } +EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Circular Easing functions EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrt(1.0f - t*t) - 1.0f) + b); } @@ -221,7 +221,7 @@ EASEDEF float EaseElasticIn(float t, float b, float c, float d) float s = p/4.0f; float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); - return (-(postFix*sin((t*d-s)*(2.0f*PI)/p )) + b); + return (-(postFix*sinf((t*d-s)*(2.0f*PI)/p )) + b); } EASEDEF float EaseElasticOut(float t, float b, float c, float d) @@ -233,7 +233,7 @@ EASEDEF float EaseElasticOut(float t, float b, float c, float d) float a = c; float s = p/4.0f; - return (a*pow(2.0f,-10.0f*t)*sin((t*d-s)*(2.0f*PI)/p) + c + b); + return (a*pow(2.0f,-10.0f*t)*sinf((t*d-s)*(2.0f*PI)/p) + c + b); } EASEDEF float EaseElasticInOut(float t, float b, float c, float d) @@ -248,12 +248,12 @@ EASEDEF float EaseElasticInOut(float t, float b, float c, float d) if (t < 1.0f) { float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); - return -0.5f*(postFix*sin((t*d-s)*(2.0f*PI)/p)) + b; + return -0.5f*(postFix*sinf((t*d-s)*(2.0f*PI)/p)) + b; } float postFix = a*pow(2.0f, -10.0f*(t-=1.0f)); - return (postFix*sin((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); + return (postFix*sinf((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); } #ifdef __cplusplus diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c index d42b3c33..9c94cfc3 100644 --- a/examples/others/easings_testbed.c +++ b/examples/others/easings_testbed.c @@ -1,57 +1,30 @@ /******************************************************************************************* * -* raylib [easings] example +* raylib [easings] example - Easings Testbed * * 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) * -* Copyright (c) 2019 Juan Miguel López +* Example contributed by Juan Miguel López (@flashback-fx) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Juan Miguel López (@flashback-fx ) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ - #include -#include "easings.h" +#include "easings.h" -// Application constants -#define SCR_WIDTH 800 -#define SCR_HEIGHT 450 -#define BALL_RADIUS 16.0f -#define BALL_COLOR MAROON -#define PAD 80.0f -#define START_X (0.0f + (BALL_RADIUS) + (PAD)) -#define END_X ((SCR_WIDTH) - ((BALL_RADIUS) + (PAD))) -#define START_Y (0.0f + (BALL_RADIUS) + (PAD)) -#define END_Y ((SCR_HEIGHT) - ((BALL_RADIUS) + (PAD))) -#define T_ADVANCE 1.0f -#define D_DFT 300.0f -#define TARGET_FPS 60 -#define BG_COLOR RAYWHITE -#define TEXT_COLOR LIGHTGRAY -#define FONT_SIZE 20 -#define D_STEP 20.0f -#define D_STEP_FINE 2.0f -#define D_MIN 1.0f -#define D_MAX 10000.0f - -// Application control keys -#define KEY_NEXT_EASE_X KEY_RIGHT -#define KEY_PREV_EASE_X KEY_LEFT -#define KEY_NEXT_EASE_Y KEY_DOWN -#define KEY_PREV_EASE_Y KEY_UP -#define KEY_INCR_D_STEP KEY_W -#define KEY_DECR_D_STEP KEY_Q -#define KEY_INCR_D_FINE KEY_S -#define KEY_DECR_D_FINE KEY_A -#define KEY_PLAY_PAUSE KEY_ENTER -#define KEY_RESTART KEY_SPACE -#define KEY_TOGGLE_UNBOUNDED_T KEY_T +#define FONT_SIZE 20 +#define D_STEP 20.0f +#define D_STEP_FINE 2.0f +#define D_MIN 1.0f +#define D_MAX 10000.0f // Easing types enum EasingTypes { - EASE_LINEAR_NONE, + EASE_LINEAR_NONE = 0, EASE_LINEAR_IN, EASE_LINEAR_OUT, EASE_LINEAR_IN_OUT, @@ -92,122 +65,35 @@ static const struct { const char *name; float (*func)(float, float, float, float); } Easings[] = { - [EASE_LINEAR_NONE] = { - .name = "EaseLinearNone", - .func = EaseLinearNone, - }, - [EASE_LINEAR_IN] = { - .name = "EaseLinearIn", - .func = EaseLinearIn, - }, - [EASE_LINEAR_OUT] = { - .name = "EaseLinearOut", - .func = EaseLinearOut, - }, - [EASE_LINEAR_IN_OUT] = { - .name = "EaseLinearInOut", - .func = EaseLinearInOut, - }, - [EASE_SINE_IN] = { - .name = "EaseSineIn", - .func = EaseSineIn, - }, - [EASE_SINE_OUT] = { - .name = "EaseSineOut", - .func = EaseSineOut, - }, - [EASE_SINE_IN_OUT] = { - .name = "EaseSineInOut", - .func = EaseSineInOut, - }, - [EASE_CIRC_IN] = { - .name = "EaseCircIn", - .func = EaseCircIn, - }, - [EASE_CIRC_OUT] = { - .name = "EaseCircOut", - .func = EaseCircOut, - }, - [EASE_CIRC_IN_OUT] = { - .name = "EaseCircInOut", - .func = EaseCircInOut, - }, - [EASE_CUBIC_IN] = { - .name = "EaseCubicIn", - .func = EaseCubicIn, - }, - [EASE_CUBIC_OUT] = { - .name = "EaseCubicOut", - .func = EaseCubicOut, - }, - [EASE_CUBIC_IN_OUT] = { - .name = "EaseCubicInOut", - .func = EaseCubicInOut, - }, - [EASE_QUAD_IN] = { - .name = "EaseQuadIn", - .func = EaseQuadIn, - }, - [EASE_QUAD_OUT] = { - .name = "EaseQuadOut", - .func = EaseQuadOut, - }, - [EASE_QUAD_IN_OUT] = { - .name = "EaseQuadInOut", - .func = EaseQuadInOut, - }, - [EASE_EXPO_IN] = { - .name = "EaseExpoIn", - .func = EaseExpoIn, - }, - [EASE_EXPO_OUT] = { - .name = "EaseExpoOut", - .func = EaseExpoOut, - }, - [EASE_EXPO_IN_OUT] = { - .name = "EaseExpoInOut", - .func = EaseExpoInOut, - }, - [EASE_BACK_IN] = { - .name = "EaseBackIn", - .func = EaseBackIn, - }, - [EASE_BACK_OUT] = { - .name = "EaseBackOut", - .func = EaseBackOut, - }, - [EASE_BACK_IN_OUT] = { - .name = "EaseBackInOut", - .func = EaseBackInOut, - }, - [EASE_BOUNCE_OUT] = { - .name = "EaseBounceOut", - .func = EaseBounceOut, - }, - [EASE_BOUNCE_IN] = { - .name = "EaseBounceIn", - .func = EaseBounceIn, - }, - [EASE_BOUNCE_IN_OUT] = { - .name = "EaseBounceInOut", - .func = EaseBounceInOut, - }, - [EASE_ELASTIC_IN] = { - .name = "EaseElasticIn", - .func = EaseElasticIn, - }, - [EASE_ELASTIC_OUT] = { - .name = "EaseElasticOut", - .func = EaseElasticOut, - }, - [EASE_ELASTIC_IN_OUT] = { - .name = "EaseElasticInOut", - .func = EaseElasticInOut, - }, - [EASING_NONE] = { - .name = "None", - .func = NoEase, - }, + [EASE_LINEAR_NONE] = { .name = "EaseLinearNone", .func = EaseLinearNone }, + [EASE_LINEAR_IN] = { .name = "EaseLinearIn", .func = EaseLinearIn }, + [EASE_LINEAR_OUT] = { .name = "EaseLinearOut", .func = EaseLinearOut }, + [EASE_LINEAR_IN_OUT] = { .name = "EaseLinearInOut", .func = EaseLinearInOut }, + [EASE_SINE_IN] = { .name = "EaseSineIn", .func = EaseSineIn }, + [EASE_SINE_OUT] = { .name = "EaseSineOut", .func = EaseSineOut }, + [EASE_SINE_IN_OUT] = { .name = "EaseSineInOut", .func = EaseSineInOut }, + [EASE_CIRC_IN] = { .name = "EaseCircIn", .func = EaseCircIn }, + [EASE_CIRC_OUT] = { .name = "EaseCircOut", .func = EaseCircOut }, + [EASE_CIRC_IN_OUT] = { .name = "EaseCircInOut", .func = EaseCircInOut }, + [EASE_CUBIC_IN] = { .name = "EaseCubicIn", .func = EaseCubicIn }, + [EASE_CUBIC_OUT] = { .name = "EaseCubicOut", .func = EaseCubicOut }, + [EASE_CUBIC_IN_OUT] = { .name = "EaseCubicInOut", .func = EaseCubicInOut }, + [EASE_QUAD_IN] = { .name = "EaseQuadIn", .func = EaseQuadIn }, + [EASE_QUAD_OUT] = { .name = "EaseQuadOut", .func = EaseQuadOut }, + [EASE_QUAD_IN_OUT] = { .name = "EaseQuadInOut", .func = EaseQuadInOut }, + [EASE_EXPO_IN] = { .name = "EaseExpoIn", .func = EaseExpoIn }, + [EASE_EXPO_OUT] = { .name = "EaseExpoOut", .func = EaseExpoOut }, + [EASE_EXPO_IN_OUT] = { .name = "EaseExpoInOut", .func = EaseExpoInOut }, + [EASE_BACK_IN] = { .name = "EaseBackIn", .func = EaseBackIn }, + [EASE_BACK_OUT] = { .name = "EaseBackOut", .func = EaseBackOut }, + [EASE_BACK_IN_OUT] = { .name = "EaseBackInOut", .func = EaseBackInOut }, + [EASE_BOUNCE_OUT] = { .name = "EaseBounceOut", .func = EaseBounceOut }, + [EASE_BOUNCE_IN] = { .name = "EaseBounceIn", .func = EaseBounceIn }, + [EASE_BOUNCE_IN_OUT] = { .name = "EaseBounceInOut", .func = EaseBounceInOut }, + [EASE_ELASTIC_IN] = { .name = "EaseElasticIn", .func = EaseElasticIn }, + [EASE_ELASTIC_OUT] = { .name = "EaseElasticOut", .func = EaseElasticOut }, + [EASE_ELASTIC_IN_OUT] = { .name = "EaseElasticInOut", .func = EaseElasticInOut }, + [EASING_NONE] = { .name = "None", .func = NoEase }, }; @@ -215,17 +101,22 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - Vector2 ballPos = { .x = START_X, .y = START_Y }; - float t = 0.0f; // Current time (in any unit measure, but same unit as duration) - float d = D_DFT; // Total time it should take to complete (duration) + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings testbed"); + + Vector2 ballPosition = { 100.0f, 200.0f }; + + float t = 0.0f; // Current time (in any unit measure, but same unit as duration) + float d = 300.0f; // Total time it should take to complete (duration) bool paused = true; - bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop + bool boundedT = true; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop - enum EasingTypes easingX = EASING_NONE; // Easing selected for x axis - enum EasingTypes easingY = EASING_NONE; // Easing selected for y axis + int easingX = EASING_NONE; // Easing selected for x axis + int easingY = EASING_NONE; // Easing selected for y axis - InitWindow(SCR_WIDTH, SCR_HEIGHT, "raylib [easings] example"); - SetTargetFPS(TARGET_FPS); + SetTargetFPS(60); //-------------------------------------------------------------------------------------- // Main game loop @@ -233,77 +124,63 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T)) - boundedT = 1 - boundedT; + if (IsKeyPressed(KEY_T)) boundedT = !boundedT; // Choose easing for the X axis - if (IsKeyPressed(KEY_NEXT_EASE_X)) + if (IsKeyPressed(KEY_RIGHT)) { - ++easingX; + easingX++; - if (easingX > EASING_NONE) - easingX = 0; + if (easingX > EASING_NONE) easingX = 0; } - else if (IsKeyPressed(KEY_PREV_EASE_X)) + else if (IsKeyPressed(KEY_LEFT)) { - if (easingX == 0) - easingX = EASING_NONE; - else - --easingX; + if (easingX == 0) easingX = EASING_NONE; + else easingX--; } // Choose easing for the Y axis - if (IsKeyPressed(KEY_NEXT_EASE_Y)) + if (IsKeyPressed(KEY_DOWN)) { - ++easingY; + easingY++; - if (easingY > EASING_NONE) - easingY = 0; + if (easingY > EASING_NONE) easingY = 0; } - else if (IsKeyPressed(KEY_PREV_EASE_Y)) + else if (IsKeyPressed(KEY_UP)) { - if (easingY == 0) - easingY = EASING_NONE; - else - --easingY; + if (easingY == 0) easingY = EASING_NONE; + else easingY--; } // Change d (duration) value - if (IsKeyPressed(KEY_INCR_D_STEP) && d < D_MAX - D_STEP) - d += D_STEP; - else if (IsKeyPressed(KEY_DECR_D_STEP) && d > D_MIN + D_STEP) - d -= D_STEP; + if (IsKeyPressed(KEY_W) && d < D_MAX - D_STEP) d += D_STEP; + else if (IsKeyPressed(KEY_Q) && d > D_MIN + D_STEP) d -= D_STEP; - if (IsKeyDown(KEY_INCR_D_FINE) && d < D_MAX - D_STEP_FINE) - d += D_STEP_FINE; - else if (IsKeyDown(KEY_DECR_D_FINE) && d > D_MIN + D_STEP_FINE) - d -= D_STEP_FINE; + if (IsKeyDown(KEY_S) && d < D_MAX - D_STEP_FINE) d += D_STEP_FINE; + else if (IsKeyDown(KEY_A) && d > D_MIN + D_STEP_FINE) d -= D_STEP_FINE; // Play, pause and restart controls - if (IsKeyPressed(KEY_RESTART) || - IsKeyPressed(KEY_NEXT_EASE_X) || IsKeyPressed(KEY_PREV_EASE_X) || - IsKeyPressed(KEY_NEXT_EASE_Y) || IsKeyPressed(KEY_PREV_EASE_Y) || - IsKeyPressed(KEY_INCR_D_STEP) || IsKeyPressed(KEY_DECR_D_STEP) || - IsKeyPressed(KEY_TOGGLE_UNBOUNDED_T) || - IsKeyDown(KEY_INCR_D_FINE) || IsKeyDown(KEY_DECR_D_FINE) || - (IsKeyPressed(KEY_PLAY_PAUSE) && boundedT == true && t >= d)) + if (IsKeyPressed(KEY_SPACE) || IsKeyPressed(KEY_T) || + IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_LEFT) || + IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_UP) || + IsKeyPressed(KEY_W) || IsKeyPressed(KEY_Q) || + IsKeyDown(KEY_S) || IsKeyDown(KEY_A) || + (IsKeyPressed(KEY_ENTER) && (boundedT == true) && (t >= d))) { t = 0.0f; - ballPos.x = START_X; - ballPos.y = START_Y; + ballPosition.x = 100.0f; + ballPosition.y = 100.0f; paused = true; } - if (IsKeyPressed(KEY_PLAY_PAUSE)) - paused = 1 - paused; + if (IsKeyPressed(KEY_ENTER)) paused = !paused; // Movement computation - if ((paused == false) && - ((boundedT == true && t < d) || boundedT == false)) + if (!paused && ((boundedT && t < d) || !boundedT)) { - ballPos.x = Easings[easingX].func(t, START_X, END_X - START_X, d); - ballPos.y = Easings[easingY].func(t, START_Y, END_Y - START_Y, d); - t += T_ADVANCE; + ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 100.0f, d); + ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 100.0f, d); + t += 1.0f; } //---------------------------------------------------------------------------------- @@ -311,25 +188,21 @@ int main(void) //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(BG_COLOR); + ClearBackground(RAYWHITE); // Draw information text - int line = 0; - - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), - 0, FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 0, FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); // Draw instructions text - line = 1; - DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use D and W or A and S keys to change duration", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); - DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, SCR_HEIGHT - FONT_SIZE * (line++), FONT_SIZE, TEXT_COLOR); + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, GetScreenHeight() - FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText("Use D and W or A and S keys to change duration", 0, GetScreenHeight() - FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, GetScreenHeight() - FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, GetScreenHeight() - FONT_SIZE*5, FONT_SIZE, LIGHTGRAY); // Draw ball - DrawCircleV(ballPos, BALL_RADIUS, BALL_COLOR); + DrawCircleV(ballPosition, 16.0f, MAROON); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/easings.h b/src/easings.h index c7ee5649..1b08af0a 100644 --- a/src/easings.h +++ b/src/easings.h @@ -90,7 +90,7 @@ #define EASEDEF extern #endif -#include // Required for: sin(), cos(), sqrt(), pow() +#include // Required for: sinf(), cosf(), sqrt(), pow() #ifndef PI #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h @@ -107,9 +107,9 @@ EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); } // Sine Easing functions -EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2.0f)) + c + b); } -EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2.0f)) + b); } -EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cos(PI*t/d) - 1.0f) + b); } +EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } +EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Circular Easing functions EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrt(1.0f - t*t) - 1.0f) + b); } @@ -221,7 +221,7 @@ EASEDEF float EaseElasticIn(float t, float b, float c, float d) float s = p/4.0f; float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); - return (-(postFix*sin((t*d-s)*(2.0f*PI)/p )) + b); + return (-(postFix*sinf((t*d-s)*(2.0f*PI)/p )) + b); } EASEDEF float EaseElasticOut(float t, float b, float c, float d) @@ -233,7 +233,7 @@ EASEDEF float EaseElasticOut(float t, float b, float c, float d) float a = c; float s = p/4.0f; - return (a*pow(2.0f,-10.0f*t)*sin((t*d-s)*(2.0f*PI)/p) + c + b); + return (a*pow(2.0f,-10.0f*t)*sinf((t*d-s)*(2.0f*PI)/p) + c + b); } EASEDEF float EaseElasticInOut(float t, float b, float c, float d) @@ -248,12 +248,12 @@ EASEDEF float EaseElasticInOut(float t, float b, float c, float d) if (t < 1.0f) { float postFix = a*pow(2.0f, 10.0f*(t-=1.0f)); - return -0.5f*(postFix*sin((t*d-s)*(2.0f*PI)/p)) + b; + return -0.5f*(postFix*sinf((t*d-s)*(2.0f*PI)/p)) + b; } float postFix = a*pow(2.0f, -10.0f*(t-=1.0f)); - return (postFix*sin((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); + return (postFix*sinf((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); } #ifdef __cplusplus -- cgit v1.2.3 From 87774a0a21f8d2998b4dc13e989005270476ae92 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 27 May 2019 00:18:15 +0200 Subject: Review variables initialization --- examples/audio/audio_module_playing.c | 2 +- examples/audio/audio_sound_loading.c | 1 - examples/core/core_2d_camera.c | 5 ++--- examples/core/core_3d_camera_first_person.c | 6 +++--- examples/core/core_3d_camera_free.c | 2 +- examples/core/core_custom_logging.c | 2 +- examples/core/core_vr_simulator.c | 2 +- examples/core/core_world_screen.c | 3 +-- examples/models/models_box_collisions.c | 2 +- examples/models/models_cubicmap.c | 2 +- examples/models/models_first_person_maze.c | 2 +- examples/models/models_heightmap.c | 2 +- examples/models/models_mesh_generation.c | 4 ++-- examples/models/models_obj_viewer.c | 2 +- examples/models/models_orthographic_projection.c | 2 +- examples/models/models_skybox.c | 2 +- examples/others/raudio_standalone.c | 2 +- examples/others/rlgl_standalone.c | 2 +- examples/physac/physics_friction.c | 4 ++-- examples/shaders/shaders_postprocessing.c | 4 ++-- examples/shapes/shapes_colors_palette.c | 2 +- examples/shapes/shapes_easings_rectangle_array.c | 2 +- examples/text/text_font_sdf.c | 2 +- examples/text/text_raylib_fonts.c | 4 ++-- examples/text/text_rectangle_bounds.c | 12 ++++++------ examples/text/text_sprite_fonts.c | 14 ++++++-------- examples/text/text_ttf_loading.c | 2 +- examples/textures/textures_background_scrolling.c | 6 +++--- examples/textures/textures_image_generation.c | 3 ++- examples/textures/textures_image_processing.c | 2 +- examples/textures/textures_particles_blending.c | 2 +- examples/textures/textures_raw_data.c | 2 +- examples/textures/textures_sprite_explosion.c | 2 +- 33 files changed, 52 insertions(+), 56 deletions(-) (limited to 'examples') diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index 4aebd1e6..4842519a 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -40,7 +40,7 @@ int main(void) YELLOW, GREEN, SKYBLUE, PURPLE, BEIGE }; // Creates ome circles for visual effect - CircleWave circles[MAX_CIRCLES]; + CircleWave circles[MAX_CIRCLES] = { 0 }; for (int i = MAX_CIRCLES - 1; i >= 0; i--) { diff --git a/examples/audio/audio_sound_loading.c b/examples/audio/audio_sound_loading.c index d208dd9a..4bc9c704 100644 --- a/examples/audio/audio_sound_loading.c +++ b/examples/audio/audio_sound_loading.c @@ -46,7 +46,6 @@ int main(void) ClearBackground(RAYWHITE); DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, LIGHTGRAY); - DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, LIGHTGRAY); EndDrawing(); diff --git a/examples/core/core_2d_camera.c b/examples/core/core_2d_camera.c index fe8e584b..81f580ad 100644 --- a/examples/core/core_2d_camera.c +++ b/examples/core/core_2d_camera.c @@ -23,8 +23,8 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); Rectangle player = { 400, 280, 40, 40 }; - Rectangle buildings[MAX_BUILDINGS]; - Color buildColors[MAX_BUILDINGS]; + Rectangle buildings[MAX_BUILDINGS] = { 0 }; + Color buildColors[MAX_BUILDINGS] = { 0 }; int spacing = 0; @@ -41,7 +41,6 @@ int main(void) } Camera2D camera = { 0 }; - camera.target = (Vector2){ player.x + 20, player.y + 20 }; camera.offset = (Vector2){ 0, 0 }; camera.rotation = 0.0f; diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index 825a9ca8..39fbfb2e 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -31,9 +31,9 @@ int main(void) camera.type = CAMERA_PERSPECTIVE; // Generates some random columns - float heights[MAX_COLUMNS]; - Vector3 positions[MAX_COLUMNS]; - Color colors[MAX_COLUMNS]; + float heights[MAX_COLUMNS] = { 0.0f }; + Vector3 positions[MAX_COLUMNS] = { 0 }; + Color colors[MAX_COLUMNS] = { 0 }; for (int i = 0; i < MAX_COLUMNS; i++) { diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index c1445f60..5053fd63 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -21,7 +21,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); // Define the camera to look into our 3d world - Camera3D camera; + Camera3D camera = { 0 }; camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) diff --git a/examples/core/core_custom_logging.c b/examples/core/core_custom_logging.c index 0ab3fa45..56e168ee 100644 --- a/examples/core/core_custom_logging.c +++ b/examples/core/core_custom_logging.c @@ -19,7 +19,7 @@ // Custom logging funtion void LogCustom(int msgType, const char *text, va_list args) { - char timeStr[64]; + char timeStr[64] = { 0 }; time_t now = time(NULL); struct tm *tm_info = localtime(&now); diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 134a36f0..628d7cf4 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -60,7 +60,7 @@ int main(void) SetVrConfiguration(hmd, distortion); // Set Vr device parameters for stereo rendering // Define the camera to look into our 3d world - Camera camera; + Camera camera = { 0 }; camera.position = (Vector3){ 5.0f, 2.0f, 5.0f }; // Camera position camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index 434952bc..31d3653d 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -29,8 +29,7 @@ int main(void) camera.type = CAMERA_PERSPECTIVE; Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - - Vector2 cubeScreenPosition; + Vector2 cubeScreenPosition = { 0.0f, 0.0f }; SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode diff --git a/examples/models/models_box_collisions.c b/examples/models/models_box_collisions.c index 0dd0710e..7a937ea7 100644 --- a/examples/models/models_box_collisions.c +++ b/examples/models/models_box_collisions.c @@ -21,7 +21,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions"); // Define the camera to look into our 3d world - Camera camera = {{ 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Vector3 playerPosition = { 0.0f, 1.0f, 2.0f }; Vector3 playerSize = { 1.0f, 2.0f, 1.0f }; diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index f399a56e..f5087845 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -21,7 +21,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing"); // Define the camera to look into our 3d world - Camera camera = {{ 16.0f, 14.0f, 16.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 16.0f, 14.0f, 16.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Image image = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM) Texture2D cubicmap = LoadTextureFromImage(image); // Convert image to texture to display (VRAM) diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index 42363747..07c51b9a 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -23,7 +23,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze"); // Define the camera to look into our 3d world - Camera camera = {{ 0.2f, 0.4f, 0.2f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 0.2f, 0.4f, 0.2f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Image imMap = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM) Texture2D cubicmap = LoadTextureFromImage(imMap); // Convert image to texture to display (VRAM) diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index e242db16..a2c2e310 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -21,7 +21,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing"); // Define our custom camera to look into our 3d world - Camera camera = {{ 18.0f, 16.0f, 18.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 18.0f, 16.0f, 18.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Image image = LoadImage("resources/heightmap.png"); // Load heightmap image (RAM) Texture2D texture = LoadTextureFromImage(image); // Convert image to texture (VRAM) diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index cfc3bdd6..6c0ae653 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -27,7 +27,7 @@ int main(void) Texture2D texture = LoadTextureFromImage(checked); UnloadImage(checked); - Model models[NUM_MODELS]; + Model models[NUM_MODELS] = { 0 }; models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5)); models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f)); @@ -42,7 +42,7 @@ int main(void) for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MAP_DIFFUSE].texture = texture; // Define the camera to look into our 3d world - Camera camera = {{ 5.0f, 5.0f, 5.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 5.0f, 5.0f, 5.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; // Model drawing position Vector3 position = { 0.0f, 0.0f, 0.0f }; diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index dc6787e7..83c8f2f1 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -23,7 +23,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib example - obj viewer"); // 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 = { { 30.0f, 30.0f, 30.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture diff --git a/examples/models/models_orthographic_projection.c b/examples/models/models_orthographic_projection.c index ca9d83c9..8c9b5b1c 100644 --- a/examples/models/models_orthographic_projection.c +++ b/examples/models/models_orthographic_projection.c @@ -28,7 +28,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes"); // Define the camera to look into our 3d world - Camera camera = {{ 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, FOVY_PERSPECTIVE, CAMERA_PERSPECTIVE }; + Camera camera = { { 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, FOVY_PERSPECTIVE, CAMERA_PERSPECTIVE }; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 1693d9bc..bad29b96 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -21,7 +21,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing"); // Define the camera to look into our 3d world - Camera camera = {{ 1.0f, 1.0f, 1.0f }, { 4.0f, 1.0f, 4.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 1.0f, 1.0f, 1.0f }, { 4.0f, 1.0f, 4.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; // Load skybox model Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); diff --git a/examples/others/raudio_standalone.c b/examples/others/raudio_standalone.c index 02720837..63d5b8d2 100644 --- a/examples/others/raudio_standalone.c +++ b/examples/others/raudio_standalone.c @@ -94,7 +94,7 @@ int main() { // Initialization //-------------------------------------------------------------------------------------- - static unsigned char key; + static unsigned char key = 0; InitAudioDevice(); diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 42aec2e2..d8100ce5 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -131,7 +131,7 @@ int main(void) rlClearColor(245, 245, 245, 255); // Define clear color rlEnableDepthTest(); // Enable DEPTH_TEST for 3D - Camera camera; + Camera camera = { 0 }; camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) diff --git a/examples/physac/physics_friction.c b/examples/physac/physics_friction.c index cbb9dc43..0acf88ae 100644 --- a/examples/physac/physics_friction.c +++ b/examples/physac/physics_friction.c @@ -61,8 +61,8 @@ int main(void) SetPhysicsBodyRotation(bodyA, 30*DEG2RAD); PhysicsBody bodyB = CreatePhysicsBodyRectangle((Vector2){ screenWidth - 35, screenHeight*0.6f }, 40, 40, 10); - bodyB->staticFriction = 1; - bodyB->dynamicFriction = 1; + bodyB->staticFriction = 1.0f; + bodyB->dynamicFriction = 1.0f; SetPhysicsBodyRotation(bodyB, 330*DEG2RAD); SetTargetFPS(60); // Set our game to run at 60 frames-per-second diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 018b8d11..ed9da8bb 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -70,7 +70,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader"); // Define the camera to look into our 3d world - Camera camera = {{ 2.0f, 3.0f, 2.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { { 2.0f, 3.0f, 2.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; Model model = LoadModel("resources/models/church.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) @@ -81,7 +81,7 @@ int main(void) // Load all postpro shaders // NOTE 1: All postpro shader use the base vertex shader (DEFAULT_VERTEX_SHADER) // NOTE 2: We load the correct shader depending on GLSL version - Shader shaders[MAX_POSTPRO_SHADERS]; + Shader shaders[MAX_POSTPRO_SHADERS] = { 0 }; // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader shaders[FX_GRAYSCALE] = LoadShader(0, FormatText("resources/shaders/glsl%i/grayscale.fs", GLSL_VERSION)); diff --git a/examples/shapes/shapes_colors_palette.c b/examples/shapes/shapes_colors_palette.c index 7322f7e5..a0bba002 100644 --- a/examples/shapes/shapes_colors_palette.c +++ b/examples/shapes/shapes_colors_palette.c @@ -45,7 +45,7 @@ int main(void) int colorState[MAX_COLORS_COUNT] = { 0 }; // Color state: 0-DEFAULT, 1-MOUSE_HOVER - Vector2 mousePoint; + Vector2 mousePoint = { 0.0f, 0.0f }; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shapes/shapes_easings_rectangle_array.c b/examples/shapes/shapes_easings_rectangle_array.c index 9844af6a..07de98a0 100644 --- a/examples/shapes/shapes_easings_rectangle_array.c +++ b/examples/shapes/shapes_easings_rectangle_array.c @@ -33,7 +33,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangle array"); - Rectangle recs[MAX_RECS_X*MAX_RECS_Y]; + Rectangle recs[MAX_RECS_X*MAX_RECS_Y] = { 0 }; for (int y = 0; y < MAX_RECS_Y; y++) { diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index d3c76c4f..91e43f75 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -57,7 +57,7 @@ int main(void) SetTextureFilter(fontSDF.texture, FILTER_BILINEAR); // Required for SDF font Vector2 fontPosition = { 40, screenHeight/2 - 50 }; - Vector2 textSize = { 0.0f }; + Vector2 textSize = { 0.0f, 0.0f }; float fontSize = 16.0f; int currentFont = 0; // 0 - fontDefault, 1 - fontSDF diff --git a/examples/text/text_raylib_fonts.c b/examples/text/text_raylib_fonts.c index 06e63725..d7e41a78 100644 --- a/examples/text/text_raylib_fonts.c +++ b/examples/text/text_raylib_fonts.c @@ -26,7 +26,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts"); // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Font fonts[MAX_FONTS]; + Font fonts[MAX_FONTS] = { 0 }; fonts[0] = LoadFont("resources/fonts/alagard.png"); fonts[1] = LoadFont("resources/fonts/pixelplay.png"); @@ -48,7 +48,7 @@ int main(void) const int spacings[MAX_FONTS] = { 2, 4, 8, 4, 3, 4, 4, 1 }; - Vector2 positions[MAX_FONTS]; + Vector2 positions[MAX_FONTS] = { 0 }; for (int i = 0; i < MAX_FONTS; i++) { diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index 5871278f..abfb142b 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -22,7 +22,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle"); - char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's\ + const char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's\ a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget."; @@ -38,15 +38,15 @@ int main(void) const int maxWidth = screenWidth - 50; const int maxHeight = screenHeight - 160; - Vector2 lastMouse = { 0, 0 }; // Stores last mouse coordinates - Color borderColor = MAROON; // Container border color - Font font = GetFontDefault(); // Get default system font + Vector2 lastMouse = { 0.0f, 0.0f }; // Stores last mouse coordinates + Color borderColor = MAROON; // Container border color + Font font = GetFontDefault(); // Get default system font - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/text/text_sprite_fonts.c b/examples/text/text_sprite_fonts.c index 30028937..b7c9ab10 100644 --- a/examples/text/text_sprite_fonts.c +++ b/examples/text/text_sprite_fonts.c @@ -29,16 +29,14 @@ int main(void) Font font2 = LoadFont("resources/custom_alagard.png"); // Font loading Font font3 = LoadFont("resources/custom_jupiter_crash.png"); // Font loading - Vector2 fontPosition1, fontPosition2, fontPosition3; + Vector2 fontPosition1 = { screenWidth/2 - MeasureTextEx(font1, msg1, font1.baseSize, -3).x/2, + screenHeight/2 - font1.baseSize/2 - 80 }; - fontPosition1.x = screenWidth/2 - MeasureTextEx(font1, msg1, font1.baseSize, -3).x/2; - fontPosition1.y = screenHeight/2 - font1.baseSize/2 - 80; + Vector2 fontPosition2 = { screenWidth/2 - MeasureTextEx(font2, msg2, font2.baseSize, -2).x/2, + screenHeight/2 - font2.baseSize/2 - 10 }; - fontPosition2.x = screenWidth/2 - MeasureTextEx(font2, msg2, font2.baseSize, -2).x/2; - fontPosition2.y = screenHeight/2 - font2.baseSize/2 - 10; - - fontPosition3.x = screenWidth/2 - MeasureTextEx(font3, msg3, font3.baseSize, 2).x/2; - fontPosition3.y = screenHeight/2 - font3.baseSize/2 + 50; + Vector2 fontPosition3 = { screenWidth/2 - MeasureTextEx(font3, msg3, font3.baseSize, 2).x/2, + screenHeight/2 - font3.baseSize/2 + 50 }; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/text/text_ttf_loading.c b/examples/text/text_ttf_loading.c index cc59417d..b256bd1d 100644 --- a/examples/text/text_ttf_loading.c +++ b/examples/text/text_ttf_loading.c @@ -33,7 +33,7 @@ int main(void) float fontSize = font.baseSize; Vector2 fontPosition = { 40, screenHeight/2 - 80 }; - Vector2 textSize; + Vector2 textSize = { 0.0f, 0.0f }; // Setup texture scaling filter SetTextureFilter(font.texture, FILTER_POINT); diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c index d91b2585..c2e5ac80 100644 --- a/examples/textures/textures_background_scrolling.c +++ b/examples/textures/textures_background_scrolling.c @@ -26,9 +26,9 @@ int main(void) Texture2D midground = LoadTexture("resources/cyberpunk_street_midground.png"); Texture2D foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); - float scrollingBack = 0; - float scrollingMid = 0; - float scrollingFore = 0; + float scrollingBack = 0.0f; + float scrollingMid = 0.0f; + float scrollingFore = 0.0f; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index d9a39a25..0bb10583 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -30,7 +30,8 @@ int main(void) Image perlinNoise = GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0f); Image cellular = GenImageCellular(screenWidth, screenHeight, 32); - Texture2D textures[NUM_TEXTURES]; + Texture2D textures[NUM_TEXTURES] = { 0 }; + textures[0] = LoadTextureFromImage(verticalGradient); textures[1] = LoadTextureFromImage(horizontalGradient); textures[2] = LoadTextureFromImage(radialGradient); diff --git a/examples/textures/textures_image_processing.c b/examples/textures/textures_image_processing.c index b9ed51d7..14442290 100644 --- a/examples/textures/textures_image_processing.c +++ b/examples/textures/textures_image_processing.c @@ -57,7 +57,7 @@ int main(void) int currentProcess = NONE; bool textureReload = false; - Rectangle selectRecs[NUM_PROCESSES]; + Rectangle selectRecs[NUM_PROCESSES] = { 0 }; for (int i = 0; i < NUM_PROCESSES; i++) selectRecs[i] = (Rectangle){ 40.0f, (float)(50 + 32*i), 150.0f, 30.0f }; diff --git a/examples/textures/textures_particles_blending.c b/examples/textures/textures_particles_blending.c index 75287ea7..d094c6c2 100644 --- a/examples/textures/textures_particles_blending.c +++ b/examples/textures/textures_particles_blending.c @@ -33,7 +33,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [textures] example - particles blending"); // Particles pool, reuse them! - Particle mouseTail[MAX_PARTICLES]; + Particle mouseTail[MAX_PARTICLES] = { 0 }; // Initialize particles for (int i = 0; i < MAX_PARTICLES; i++) diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index 17604bde..08269bf7 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -31,7 +31,7 @@ int main(void) Texture2D fudesumi = LoadTextureFromImage(fudesumiRaw); // Upload CPU (RAM) image to GPU (VRAM) UnloadImage(fudesumiRaw); // Unload CPU (RAM) image data - // Generate a checked texture by code (1024x1024 pixels) + // Generate a checked texture by code int width = 960; int height = 480; diff --git a/examples/textures/textures_sprite_explosion.c b/examples/textures/textures_sprite_explosion.c index 58a8f6fc..823c1b8a 100644 --- a/examples/textures/textures_sprite_explosion.c +++ b/examples/textures/textures_sprite_explosion.c @@ -38,7 +38,7 @@ int main(void) int currentLine = 0; Rectangle frameRec = { 0, 0, frameWidth, frameHeight }; - Vector2 position = { 0, 0 }; + Vector2 position = { 0.0f, 0.0f }; bool active = false; int framesCounter = 0; -- cgit v1.2.3 From efdc6f87d53d319816b3fc76a00f309f110f3955 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 29 May 2019 13:47:57 +0200 Subject: Define standard examples size --- examples/core/core_vr_simulator.c | 5 +++-- games/Makefile | 4 ++-- src/Makefile | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 628d7cf4..76e22b1a 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -21,11 +21,12 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 1080; - const int screenHeight = 600; + const int screenWidth = 800; + const int screenHeight = 450; // NOTE: screenWidth/screenHeight should match VR device aspect ratio + SetConfigFlags(FLAG_MSAA_4X_HINT); InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); // Init VR simulator (Oculus Rift CV1 parameters) diff --git a/games/Makefile b/games/Makefile index d69b895c..3c9c488d 100644 --- a/games/Makefile +++ b/games/Makefile @@ -27,7 +27,7 @@ PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 2.5.0 RAYLIB_API_VERSION ?= 2 -RAYLIB_PATH ?= C:\GitHub\raylib +RAYLIB_PATH ?= D:\GitHub\raylib # Define default options @@ -115,7 +115,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables - EMSDK_PATH ?= C:/emsdk + EMSDK_PATH ?= D:/emsdk EMSCRIPTEN_VERSION ?= 1.38.31 CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 diff --git a/src/Makefile b/src/Makefile index 8198e147..9411b0e2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -147,7 +147,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables - EMSDK_PATH ?= C:/emsdk + EMSDK_PATH ?= D:/emsdk EMSCRIPTEN_VERSION ?= 1.38.31 CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 -- cgit v1.2.3 From 42d57bbe00c99fe3cf6a0819b9a2bf4bf3b64873 Mon Sep 17 00:00:00 2001 From: Chris Dill Date: Wed, 29 May 2019 13:58:31 +0100 Subject: Added array bounds check to textures_bunnymark --- examples/textures/textures_bunnymark.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index 6b91d646..784417d2 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -54,13 +54,16 @@ int main(void) // Create more bunnies for (int i = 0; i < 100; i++) { - bunnies[bunniesCount].position = GetMousePosition(); - bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250)/60.0f; - bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250)/60.0f; - bunnies[bunniesCount].color = (Color){ GetRandomValue(50, 240), + if (bunniesCount < MAX_BUNNIES) + { + bunnies[bunniesCount].position = GetMousePosition(); + bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250)/60.0f; + bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250)/60.0f; + bunnies[bunniesCount].color = (Color){ GetRandomValue(50, 240), GetRandomValue(80, 240), GetRandomValue(100, 240), 255 }; - bunniesCount++; + bunniesCount++; + } } } -- cgit v1.2.3 From a9f33c9a8962735fed5dd1857709d159bc4056fc Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 31 May 2019 10:03:44 +0200 Subject: Reduce textures size --- .../models/resources/models/castle_diffuse.png | Bin 1538906 -> 444546 bytes examples/models/resources/pbr/trooper_albedo.png | Bin 7676508 -> 1838043 bytes examples/models/resources/pbr/trooper_ao.png | Bin 1832292 -> 559997 bytes examples/models/resources/pbr/trooper_normals.png | Bin 4959793 -> 1313366 bytes .../models/resources/pbr/trooper_roughness.png | Bin 2862048 -> 733780 bytes 5 files changed, 0 insertions(+), 0 deletions(-) (limited to 'examples') diff --git a/examples/models/resources/models/castle_diffuse.png b/examples/models/resources/models/castle_diffuse.png index b616e1dd..361144e9 100644 Binary files a/examples/models/resources/models/castle_diffuse.png and b/examples/models/resources/models/castle_diffuse.png differ diff --git a/examples/models/resources/pbr/trooper_albedo.png b/examples/models/resources/pbr/trooper_albedo.png index ac1422e4..9ba0f5a6 100644 Binary files a/examples/models/resources/pbr/trooper_albedo.png and b/examples/models/resources/pbr/trooper_albedo.png differ diff --git a/examples/models/resources/pbr/trooper_ao.png b/examples/models/resources/pbr/trooper_ao.png index 8567f7b4..442dd7fb 100644 Binary files a/examples/models/resources/pbr/trooper_ao.png and b/examples/models/resources/pbr/trooper_ao.png differ diff --git a/examples/models/resources/pbr/trooper_normals.png b/examples/models/resources/pbr/trooper_normals.png index 59c7bdc4..e04be883 100644 Binary files a/examples/models/resources/pbr/trooper_normals.png and b/examples/models/resources/pbr/trooper_normals.png differ diff --git a/examples/models/resources/pbr/trooper_roughness.png b/examples/models/resources/pbr/trooper_roughness.png index 53186d51..29f418f7 100644 Binary files a/examples/models/resources/pbr/trooper_roughness.png and b/examples/models/resources/pbr/trooper_roughness.png differ -- cgit v1.2.3 From 434e460c18e7e47e154aff42196a3456aacd9e1c Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 31 May 2019 16:42:16 +0200 Subject: Example instructions tweak for clarity --- examples/core/core_window_letterbox.c | 2 +- examples/text/text_bmfont_ttf.c | 2 +- examples/text/text_font_sdf.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/core/core_window_letterbox.c b/examples/core/core_window_letterbox.c index f90214cf..7ee1a832 100644 --- a/examples/core/core_window_letterbox.c +++ b/examples/core/core_window_letterbox.c @@ -66,7 +66,7 @@ int main(void) for (int i = 0; i < 10; i++) DrawRectangle(0, (gameScreenHeight/10)*i, gameScreenWidth, gameScreenHeight/10, colors[i]); - DrawText("You can resize the window,\nand see the screen scaling!", 10, 25, 20, WHITE); + DrawText("If executed inside a window,\nyou can resize the window,\nand see the screen scaling!", 10, 25, 20, WHITE); EndTextureMode(); diff --git a/examples/text/text_bmfont_ttf.c b/examples/text/text_bmfont_ttf.c index 175d3f16..0fc82e97 100644 --- a/examples/text/text_bmfont_ttf.c +++ b/examples/text/text_bmfont_ttf.c @@ -53,7 +53,7 @@ int main(void) ClearBackground(RAYWHITE); - DrawText("Press SPACE to use TTF generated font", 20, 20, 20, LIGHTGRAY); + DrawText("Hold SPACE to use TTF generated font", 20, 20, 20, LIGHTGRAY); if (!useTtf) { diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 91e43f75..41ce3b2a 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -111,7 +111,7 @@ int main(void) DrawText(FormatText("RENDER SIZE: %02.02f", fontSize), GetScreenWidth() - 240, 50, 20, DARKGRAY); DrawText("Use MOUSE WHEEL to SCALE TEXT!", GetScreenWidth() - 240, 90, 10, DARKGRAY); - DrawText("PRESS SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, MAROON); + DrawText("HOLD SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, MAROON); EndDrawing(); //---------------------------------------------------------------------------------- -- cgit v1.2.3 From 923f4b9bbd1af4bff0037807ec0f6e6514c043cd Mon Sep 17 00:00:00 2001 From: Codecat Date: Wed, 5 Jun 2019 10:35:20 +0200 Subject: Added waving cubes example --- examples/models/models_waving_cubes.c | 105 ++++++++++++++++++++++++++++++++ examples/models/models_waving_cubes.png | Bin 0 -> 37712 bytes 2 files changed, 105 insertions(+) create mode 100644 examples/models/models_waving_cubes.c create mode 100644 examples/models/models_waving_cubes.png (limited to 'examples') diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c new file mode 100644 index 00000000..0fefbcf2 --- /dev/null +++ b/examples/models/models_waving_cubes.c @@ -0,0 +1,105 @@ +/******************************************************************************************* +* +* raylib [models] example - Waving cubes +* +* 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) +* +* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - waving cubes"); + + // Initialize the camera + Camera3D camera; + camera.position = (Vector3){ 30.0f, 20.0f, 30.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 70.0f; + camera.type = CAMERA_PERSPECTIVE; + + // Specify the amount of blocks in each direction + const int numBlocks = 15; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + double time = GetTime(); + + // Calculate time scale for cube position and size + float scale = (2.0f + (float)sin(time)) * 0.7f; + + // Move camera around the scene + double cameraTime = time * 0.3; + camera.position.x = (float)cos(cameraTime) * 40.0f; + camera.position.z = (float)sin(cameraTime) * 40.0f; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawGrid(10, 5.0f); + + for (int x = 0; x < numBlocks; x++) { + for (int y = 0; y < numBlocks; y++) { + for (int z = 0; z < numBlocks; z++) { + // Scale of the blocks depends on x/y/z positions + float blockScale = (x + y + z) / 30.0f; + + // Scatter makes the waving effect by adding blockScale over time + float scatter = sinf(blockScale * 20.0f + (float)(time * 4.0f)); + + // Calculate the cube position + Vector3 cubePos = { + (float)(x - numBlocks / 2) * (scale * 3.0f) + scatter, + (float)(y - numBlocks / 2) * (scale * 2.0f) + scatter, + (float)(z - numBlocks / 2) * (scale * 3.0f) + scatter + }; + + // Pick a color with a hue depending on cube position for the rainbow color effect + Color cubeColor = ColorFromHSV((Vector3){ (float)(((x + y + z) * 18) % 360), 0.75f, 0.9f }); + + // Calculate cube size + float cubeSize = (2.4f - scale) * blockScale; + + // And finally, draw the cube! + DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor); + } + } + } + + DrawFPS(10, 10); + + EndMode3D(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_waving_cubes.png b/examples/models/models_waving_cubes.png new file mode 100644 index 00000000..37a1761e Binary files /dev/null and b/examples/models/models_waving_cubes.png differ -- cgit v1.2.3 From ddaa4a304d5a8fe8bf7fc6d4d94a2bcd01298119 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Jun 2019 12:58:35 +0200 Subject: Review contributor info --- examples/models/models_animation.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index 294b07a5..7f38b7f5 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -5,7 +5,9 @@ * 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) * -* Copyright (c) 2019 Ramon Santamaria (@raysan5) and @culacant +* Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Culacant (@culacant) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ -- cgit v1.2.3 From 03720b30a135d9acab1f008c7ffb2aa46f8dbffb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Jun 2019 12:58:53 +0200 Subject: Review contributed example --- examples/models/models_waving_cubes.c | 47 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'examples') diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c index 0fefbcf2..f6309bd6 100644 --- a/examples/models/models_waving_cubes.c +++ b/examples/models/models_waving_cubes.c @@ -5,7 +5,9 @@ * 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) * -* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* Example contributed by Codecat (@codecat) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Codecat (@codecat) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -23,7 +25,7 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [models] example - waving cubes"); // Initialize the camera - Camera3D camera; + Camera3D camera = { 0 }; camera.position = (Vector3){ 30.0f, 20.0f, 30.0f }; camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; @@ -40,16 +42,18 @@ int main() while (!WindowShouldClose()) // Detect window close button or ESC key { // Update + //---------------------------------------------------------------------------------- double time = GetTime(); // Calculate time scale for cube position and size - float scale = (2.0f + (float)sin(time)) * 0.7f; + float scale = (2.0f + (float)sin(time))*0.7f; // Move camera around the scene - double cameraTime = time * 0.3; - camera.position.x = (float)cos(cameraTime) * 40.0f; - camera.position.z = (float)sin(cameraTime) * 40.0f; - + double cameraTime = time*0.3; + camera.position.x = (float)cos(cameraTime)*40.0f; + camera.position.z = (float)sin(cameraTime)*40.0f; + //---------------------------------------------------------------------------------- + // Draw //---------------------------------------------------------------------------------- BeginDrawing(); @@ -60,37 +64,40 @@ int main() DrawGrid(10, 5.0f); - for (int x = 0; x < numBlocks; x++) { - for (int y = 0; y < numBlocks; y++) { - for (int z = 0; z < numBlocks; z++) { + for (int x = 0; x < numBlocks; x++) + { + for (int y = 0; y < numBlocks; y++) + { + for (int z = 0; z < numBlocks; z++) + { // Scale of the blocks depends on x/y/z positions - float blockScale = (x + y + z) / 30.0f; + float blockScale = (x + y + z)/30.0f; // Scatter makes the waving effect by adding blockScale over time - float scatter = sinf(blockScale * 20.0f + (float)(time * 4.0f)); + float scatter = sinf(blockScale*20.0f + (float)(time*4.0f)); // Calculate the cube position Vector3 cubePos = { - (float)(x - numBlocks / 2) * (scale * 3.0f) + scatter, - (float)(y - numBlocks / 2) * (scale * 2.0f) + scatter, - (float)(z - numBlocks / 2) * (scale * 3.0f) + scatter + (float)(x - numBlocks/2)*(scale*3.0f) + scatter, + (float)(y - numBlocks/2)*(scale*2.0f) + scatter, + (float)(z - numBlocks/2)*(scale*3.0f) + scatter }; // Pick a color with a hue depending on cube position for the rainbow color effect - Color cubeColor = ColorFromHSV((Vector3){ (float)(((x + y + z) * 18) % 360), 0.75f, 0.9f }); + Color cubeColor = ColorFromHSV((Vector3){ (float)(((x + y + z)*18)%360), 0.75f, 0.9f }); // Calculate cube size - float cubeSize = (2.4f - scale) * blockScale; + float cubeSize = (2.4f - scale)*blockScale; // And finally, draw the cube! DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor); } } } - - DrawFPS(10, 10); - + EndMode3D(); + + DrawFPS(10, 10); EndDrawing(); //---------------------------------------------------------------------------------- -- cgit v1.2.3 From bdbc05c0dc255d36c74415b6802a58178e821943 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Jun 2019 13:35:22 +0200 Subject: Review font --- examples/text/resources/pixantiqua.fnt | 2 +- examples/text/resources/pixantiqua.png | Bin 0 -> 4531 bytes examples/text/resources/pixantiqua_0.png | Bin 4531 -> 0 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 examples/text/resources/pixantiqua.png delete mode 100644 examples/text/resources/pixantiqua_0.png (limited to 'examples') diff --git a/examples/text/resources/pixantiqua.fnt b/examples/text/resources/pixantiqua.fnt index 971b9b0b..fd9f9dbb 100644 --- a/examples/text/resources/pixantiqua.fnt +++ b/examples/text/resources/pixantiqua.fnt @@ -1,6 +1,6 @@ info face="PixAntiqua" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=2,2,2,2 spacing=2,2 outline=0 common lineHeight=32 base=27 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 -page id=0 file="pixantiqua_0.png" +page id=0 file="pixantiqua.png" chars count=184 char id=32 x=9 y=304 width=7 height=36 xoffset=-3 yoffset=-2 xadvance=8 page=0 chnl=15 char id=33 x=391 y=266 width=11 height=36 xoffset=-3 yoffset=-2 xadvance=8 page=0 chnl=15 diff --git a/examples/text/resources/pixantiqua.png b/examples/text/resources/pixantiqua.png new file mode 100644 index 00000000..2aa2870f Binary files /dev/null and b/examples/text/resources/pixantiqua.png differ diff --git a/examples/text/resources/pixantiqua_0.png b/examples/text/resources/pixantiqua_0.png deleted file mode 100644 index 2aa2870f..00000000 Binary files a/examples/text/resources/pixantiqua_0.png and /dev/null differ -- cgit v1.2.3 From 63e320d40524825f5632b4bb42017035aaf793ef Mon Sep 17 00:00:00 2001 From: XiaochuanWang Date: Sun, 9 Jun 2019 01:08:10 +1000 Subject: Update Makefile a minor issue: where the location of the file "raylib.rc.data" is wrong, and this will cause an error in compilation. --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile index e79bc364..02b55821 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -200,7 +200,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) # resource file contains windows executable icon and properties # -Wl,--subsystem,windows hides the console window - CFLAGS += $(RAYLIB_PATH)/raylib.rc.data -Wl,--subsystem,windows + CFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data -Wl,--subsystem,windows endif ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),STATIC) -- cgit v1.2.3