aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorraysan5 <raysan5@gmail.com>2015-05-04 23:46:31 +0200
committerraysan5 <raysan5@gmail.com>2015-05-04 23:46:31 +0200
commiteae98e1c34512579d69966c99713bd0c45bfcb50 (patch)
tree86b224238a9a017d45e5267b59f755b1c6dfbd1a /src
parentba257b09f55f240ec48200383fe49ca4bf3e1cab (diff)
downloadraylib-eae98e1c34512579d69966c99713bd0c45bfcb50.tar.gz
raylib-eae98e1c34512579d69966c99713bd0c45bfcb50.zip
Big batch of changes, check description:
- Camera system moved to a separate module [camera.c] - WIP: Added customization functions for camera controls - Added custom shaders support on batch drawing - Complete redesign of textures module to support multiple texture formats (compressed and uncompressed)
Diffstat (limited to 'src')
-rw-r--r--src/camera.c476
-rw-r--r--src/core.c406
-rw-r--r--src/gestures.c50
-rw-r--r--src/models.c116
-rw-r--r--src/raylib.h75
-rw-r--r--src/rlgl.c158
-rw-r--r--src/rlgl.h2
-rw-r--r--src/text.c37
-rw-r--r--src/textures.c883
9 files changed, 1331 insertions, 872 deletions
diff --git a/src/camera.c b/src/camera.c
new file mode 100644
index 00000000..b960afdf
--- /dev/null
+++ b/src/camera.c
@@ -0,0 +1,476 @@
+/**********************************************************************************************
+*
+* raylib.camera
+*
+* Camera Modes Setup and Control Functions
+*
+* Copyright (c) 2015 Marc Palau and 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.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// CAMERA_GENERIC
+#define CAMERA_SCROLL_SENSITIVITY 1.5
+
+// FREE_CAMERA
+#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01
+#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3
+#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12
+#define FREE_CAMERA_MIN_CLAMP 85
+#define FREE_CAMERA_MAX_CLAMP -85
+#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05
+#define FREE_CAMERA_PANNING_DIVIDER 5.1
+
+// ORBITAL_CAMERA
+#define ORBITAL_CAMERA_SPEED 0.01
+
+// FIRST_PERSON
+//#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003
+#define FIRST_PERSON_FOCUS_DISTANCE 25
+#define FIRST_PERSON_MIN_CLAMP 85
+#define FIRST_PERSON_MAX_CLAMP -85
+
+#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0
+#define FIRST_PERSON_STEP_DIVIDER 30.0
+#define FIRST_PERSON_WAVING_DIVIDER 200.0
+
+#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85
+
+// THIRD_PERSON
+//#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003
+#define THIRD_PERSON_DISTANCE_CLAMP 1.2
+#define THIRD_PERSON_MIN_CLAMP 5
+#define THIRD_PERSON_MAX_CLAMP -85
+#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 }
+
+// PLAYER (used by camera)
+#define PLAYER_WIDTH 0.4
+#define PLAYER_HEIGHT 0.9
+#define PLAYER_DEPTH 0.4
+#define PLAYER_MOVEMENT_DIVIDER 20.0
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}};
+static Vector2 cameraAngle = { 0, 0 };
+static float cameraTargetDistance = 5;
+static Vector3 resetingPosition = { 0, 0, 0 };
+static int resetingKey = 'Z';
+static Vector2 cameraMousePosition = { 0, 0 };
+static Vector2 cameraMouseVariation = { 0, 0 };
+static float mouseSensitivity = 0.003;
+static int cameraMovementController[6] = { 'W', 'A', 'S', 'D', 'E', 'Q' };
+static int cameraMovementCounter = 0;
+static bool cameraUseGravity = true;
+static int pawnControllingKey = MOUSE_MIDDLE_BUTTON;
+static int fnControllingKey = KEY_LEFT_ALT;
+static int smoothZoomControllingKey = KEY_LEFT_CONTROL;
+
+static int cameraMode = CAMERA_CUSTOM;
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+static void ProcessCamera(Camera *camera, Vector3 *playerPosition);
+/*
+static void SetCameraControls(int front, int left, int back, right, up, down);
+static void SetMouseSensitivity(int sensitivity);
+static void SetResetPosition(Vector3 resetPosition);
+static void SetResetControl(int resetKey);
+static void SetPawnControl(int pawnControlKey);
+static void SetFnControl(int fnControlKey);
+static void SetSmoothZoomControl(int smoothZoomControlKey);
+*/
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Select camera mode (multiple camera modes available)
+void SetCameraMode(int mode)
+{
+ if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE))
+ {
+ cameraMode = CAMERA_THIRD_PERSON;
+ cameraTargetDistance = 5;
+ cameraAngle.y = -40 * DEG2RAD;
+ ProcessCamera(&internalCamera, &internalCamera.position);
+ }
+ else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL))
+ {
+ cameraMode = CAMERA_THIRD_PERSON;
+ cameraTargetDistance = 5;
+ cameraAngle.y = -40 * DEG2RAD;
+ ProcessCamera(&internalCamera, &internalCamera.position);
+ }
+ else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE))
+ {
+ cameraTargetDistance = 10;
+ cameraAngle.x = 45 * DEG2RAD;
+ cameraAngle.y = -40 * DEG2RAD;
+ internalCamera.target = (Vector3){ 0, 0, 0};
+ ProcessCamera(&internalCamera, &internalCamera.position);
+ }
+ else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL))
+ {
+ cameraTargetDistance = 10;
+ cameraAngle.x = 225 * DEG2RAD;
+ cameraAngle.y = -40 * DEG2RAD;
+ internalCamera.target = (Vector3){ 3, 0, 3};
+ ProcessCamera(&internalCamera, &internalCamera.position);
+ }
+
+ cameraMode = mode;
+}
+
+// Update camera with position
+Camera UpdateCamera(Vector3 *position)
+{
+ // Calculate camera
+ if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position);
+
+ return internalCamera;
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
+// Process desired camera mode and controls
+static void ProcessCamera(Camera *camera, Vector3 *playerPosition)
+{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI)
+ // Mouse movement detection
+ if (cameraMode != CAMERA_FREE)
+ {
+ HideCursor();
+
+ if (GetMousePosition().x < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetScreenWidth() - GetScreenHeight() / 3, GetMousePosition().y});
+ else if (GetMousePosition().y < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() - GetScreenHeight() / 3});
+ else if (GetMousePosition().x > GetScreenWidth() - GetScreenHeight() / 3) SetMousePosition((Vector2) { GetScreenHeight() / 3, GetMousePosition().y});
+ else if (GetMousePosition().y > GetScreenHeight() - GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() / 3});
+ else
+ {
+ cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x;
+ cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y;
+ }
+ }
+ else
+ {
+ ShowCursor();
+
+ cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x;
+ cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y;
+ }
+
+ cameraMousePosition = GetMousePosition();
+
+ // Support for multiple automatic camera modes
+ switch (cameraMode)
+ {
+ case CAMERA_FREE:
+ {
+ // Camera zoom
+ if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
+ {
+ cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
+
+ if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP;
+ }
+ // Camera looking down
+ else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
+ {
+ camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ }
+ else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0))
+ {
+ camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+
+ if (camera->target.y < 0) camera->target.y = -0.001;
+ }
+ else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0))
+ {
+ cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
+ if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
+ }
+ // Camera looking up
+ else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
+ {
+ camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ }
+ else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0))
+ {
+ camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+ camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
+
+ if (camera->target.y > 0) camera->target.y = 0.001;
+ }
+ else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0))
+ {
+ cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
+ if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
+ }
+
+ // Inputs
+ if (IsKeyDown(fnControllingKey))
+ {
+ if (IsKeyDown(smoothZoomControllingKey))
+ {
+ // Camera smooth zoom
+ if (IsMouseButtonDown(pawnControllingKey)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY);
+ }
+ // Camera orientation calculation
+ else if (IsMouseButtonDown(pawnControllingKey))
+ {
+ // Camera orientation calculation
+ // Get the mouse sensitivity
+ cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY;
+ cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY;
+
+ // Angle clamp
+ if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD;
+ else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD;
+ }
+ }
+ // Paning
+ else if (IsMouseButtonDown(pawnControllingKey))
+ {
+ camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
+ camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
+ camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
+ }
+
+ // Focus to center
+ if (IsKeyDown(resetingKey)) camera->target = resetingPosition;
+
+ // Camera position update
+ camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
+
+ if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+ else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+
+ camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
+
+ } break;
+ case CAMERA_ORBITAL:
+ {
+ cameraAngle.x += ORBITAL_CAMERA_SPEED;
+
+ // Camera zoom
+ cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
+ // Camera distance clamp
+ if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
+
+ // Focus to center
+ if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 };
+
+ // Camera position update
+ camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
+
+ if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+ else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+
+ camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
+
+ } break;
+ case CAMERA_FIRST_PERSON:
+ case CAMERA_THIRD_PERSON:
+ {
+ bool isMoving = false;
+
+ // Keyboard inputs
+ if (IsKeyDown(cameraMovementController[0]))
+ {
+ playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER;
+
+ isMoving = true;
+ }
+ else if (IsKeyDown(cameraMovementController[2]))
+ {
+ playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER;
+
+ isMoving = true;
+ }
+
+ if (IsKeyDown(cameraMovementController[1]))
+ {
+ playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+
+ isMoving = true;
+ }
+ else if (IsKeyDown(cameraMovementController[3]))
+ {
+ playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+ playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
+
+ isMoving = true;
+ }
+
+ if (IsKeyDown(cameraMovementController[4]))
+ {
+ if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER;
+ }
+ else if (IsKeyDown(cameraMovementController[5]))
+ {
+ if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER;
+ }
+
+ if (cameraMode == CAMERA_THIRD_PERSON)
+ {
+ // Camera orientation calculation
+ // Get the mouse sensitivity
+ cameraAngle.x += cameraMouseVariation.x * -mouseSensitivity;
+ cameraAngle.y += cameraMouseVariation.y * -mouseSensitivity;
+
+ // Angle clamp
+ if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD;
+ else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD;
+
+ // Camera zoom
+ cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
+
+ // Camera distance clamp
+ if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
+
+ // Camera is always looking at player
+ camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x);
+ camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y;
+ camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x);
+
+ // Camera position update
+ camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
+
+ if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+ else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
+
+ camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
+ }
+ else
+ {
+ if (isMoving) cameraMovementCounter++;
+
+ // Camera orientation calculation
+ // Get the mouse sensitivity
+ cameraAngle.x += cameraMouseVariation.x * -mouseSensitivity;
+ cameraAngle.y += cameraMouseVariation.y * -mouseSensitivity;
+
+ // Angle clamp
+ if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD;
+ else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD;
+
+ // Camera is always looking at player
+ camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE;
+ camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE;
+ camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE;
+
+ camera->position.x = playerPosition->x;
+ camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER;
+ camera->position.z = playerPosition->z;
+
+ camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER;
+ camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER;
+ }
+ } break;
+ default: break;
+ }
+#endif
+}
+
+void SetCameraControls(int frontKey, int leftKey, int backKey, int rightKey, int upKey, int downKey)
+{
+ cameraMovementController[0] = frontKey;
+ cameraMovementController[1] = leftKey;
+ cameraMovementController[2] = backKey;
+ cameraMovementController[3] = rightKey;
+ cameraMovementController[4] = upKey;
+ cameraMovementController[5] = downKey;
+}
+
+void SetMouseSensitivity(float sensitivity)
+{
+ mouseSensitivity = (sensitivity / 10000.0);
+}
+
+void SetResetPosition(Vector3 resetPosition)
+{
+ resetingPosition = resetPosition;
+}
+
+void SetResetControl(int resetKey)
+{
+ resetingKey = resetKey;
+}
+
+void SetPawnControl(int pawnControlKey)
+{
+ pawnControllingKey = pawnControlKey;
+}
+
+void SetFnControl(int fnControlKey)
+{
+ fnControllingKey = fnControlKey;
+}
+
+void SetSmoothZoomControl(int smoothZoomControlKey)
+{
+ smoothZoomControllingKey = smoothZoomControlKey;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core.c b/src/core.c
index d83c14f7..384e043a 100644
--- a/src/core.c
+++ b/src/core.c
@@ -45,7 +45,7 @@
#include <stdio.h> // Standard input / output lib
#include <stdlib.h> // Declares malloc() and free() for memory management, rand(), atexit()
#include <stdint.h> // Required for typedef unsigned long long int uint64_t, used by hi-res timer
-#include <time.h> // Useful to initialize random seed - Android/RPI hi-res timer
+#include <time.h> // Useful to initialize random seed - Android/RPI hi-res timer (NOTE: Linux only!)
#include <math.h> // Math related functions, tan() used to set perspective
#include <string.h> // String function definitions, memset()
#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
@@ -99,48 +99,6 @@
//----------------------------------------------------------------------------------
#define MAX_TOUCH_POINTS 256
-// Camera System configuration
-//----------------------------------------------------------------------------------
-// CAMERA_GENERIC
-#define CAMERA_SCROLL_SENSITIVITY 1.5
-
-// FREE_CAMERA
-#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01
-#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3
-#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12
-#define FREE_CAMERA_MIN_CLAMP 85
-#define FREE_CAMERA_MAX_CLAMP -85
-#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05
-#define FREE_CAMERA_PANNING_DIVIDER 5.1
-
-// ORBITAL_CAMERA
-#define ORBITAL_CAMERA_SPEED 0.01
-
-// FIRST_PERSON
-#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003
-#define FIRST_PERSON_FOCUS_DISTANCE 25
-#define FIRST_PERSON_MIN_CLAMP 85
-#define FIRST_PERSON_MAX_CLAMP -85
-
-#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0
-#define FIRST_PERSON_STEP_DIVIDER 30.0
-#define FIRST_PERSON_WAVING_DIVIDER 200.0
-
-#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85
-
-// THIRD_PERSON
-#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003
-#define THIRD_PERSON_DISTANCE_CLAMP 1.2
-#define THIRD_PERSON_MIN_CLAMP 5
-#define THIRD_PERSON_MAX_CLAMP -85
-#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 }
-
-// PLAYER (used by camera)
-#define PLAYER_WIDTH 0.4
-#define PLAYER_HEIGHT 0.9
-#define PLAYER_DEPTH 0.4
-#define PLAYER_MOVEMENT_DIVIDER 20.0
-
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -207,7 +165,6 @@ static bool cursorOnScreen = false; // Tracks if cursor is inside client
static Texture2D cursor; // Cursor texture
static Vector2 mousePosition;
-static bool cursorHidden;
static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once
static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once
@@ -225,6 +182,8 @@ static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
static int lastKeyPressed = -1;
#endif
+static bool cursorHidden;
+
static double currentTime, previousTime; // Used to track timmings
static double updateTime, drawTime; // Time measures for update and draw
static double frameTime; // Time measure for one frame
@@ -233,17 +192,6 @@ static double targetTime = 0.0; // Desired time for one frame, if 0
static char configFlags = 0;
static bool showLogo = false;
-// Camera variables
-static int cameraMode = CAMERA_CUSTOM;
-static Camera currentCamera;
-static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}};
-static Vector2 cameraAngle = { 0, 0 };
-static float cameraTargetDistance = 5;
-static Vector2 cameraMousePosition = { 0, 0 };
-static Vector2 cameraMouseVariation = { 0, 0 };
-static int cameraMovementCounter = 0;
-static bool cameraUseGravity = true;
-
// Shaders variables
static bool enabledPostpro = false;
@@ -306,8 +254,6 @@ static void TakeScreenshot(void);
static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
#endif
-static void ProcessCamera(Camera *camera, Vector3 *playerPosition);
-
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
//----------------------------------------------------------------------------------
@@ -600,10 +546,7 @@ void Begin3dMode(Camera camera)
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
// Setup Camera view
- if (cameraMode == CAMERA_CUSTOM) currentCamera = camera;
- else currentCamera = internalCamera;
-
- Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up);
+ Matrix view = MatrixLookAt(camera.position, camera.target, camera.up);
rlMultMatrixf(GetMatrixVector(view)); // Multiply MODELVIEW matrix by view matrix (camera)
}
@@ -707,7 +650,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
Ray ray;
Matrix proj = MatrixIdentity();
- Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up);
+ Matrix view = MatrixLookAt(camera.position, camera.target, camera.up);
float aspect = (GLfloat)GetScreenWidth()/(GLfloat)GetScreenHeight();
double top = 0.1f*tanf(45.0f*PI / 360.0f);
@@ -725,7 +668,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
Vector3 nearPoint = { mousePosition.x, realy, 0.0f };
Vector3 farPoint = { mousePosition.x, realy, 1.0f };
- nearPoint = internalCamera.position;
+ //nearPoint = internalCamera.position;
farPoint = rlglUnproject(farPoint, proj, view);
Vector3 direction = VectorSubtract(farPoint, nearPoint);
@@ -734,72 +677,9 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
ray.position = nearPoint;
ray.direction = direction;
- // Test
- Vector2 screenPos;
- screenPos.x = (mousePosition.x / (float)GetScreenWidth() * 2.0) - 1.0f;
- screenPos.y = (mousePosition.y / (float)GetScreenHeight() * 2.0) - 1.0f;
-
- direction = VectorSubtract(internalCamera.target, internalCamera.position);
-
- printf("/nScreenPos %f, %f", screenPos.x, screenPos.y);
-
- Matrix rotate;
- rotate = MatrixIdentity();
- rotate = MatrixRotate(0, 45*DEG2RAD*screenPos.y, 0);
- VectorTransform(&direction, rotate);
-
- VectorNormalize(&direction);
-
- ray.position = internalCamera.position;
- ray.direction = direction;
-
return ray;
}
-void SetCameraMode(int mode)
-{
- if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE))
- {
- cameraMode = CAMERA_THIRD_PERSON;
- cameraTargetDistance = 5;
- cameraAngle.y = -40 * DEG2RAD;
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
- else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL))
- {
- cameraMode = CAMERA_THIRD_PERSON;
- cameraTargetDistance = 5;
- cameraAngle.y = -40 * DEG2RAD;
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
- else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE))
- {
- cameraTargetDistance = 10;
- cameraAngle.x = 45 * DEG2RAD;
- cameraAngle.y = -40 * DEG2RAD;
- internalCamera.target = (Vector3){ 0, 0, 0};
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
- else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL))
- {
- cameraTargetDistance = 10;
- cameraAngle.x = 225 * DEG2RAD;
- cameraAngle.y = -40 * DEG2RAD;
- internalCamera.target = (Vector3){ 3, 0, 3};
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
-
- cameraMode = mode;
-}
-
-Camera UpdateCamera(Vector3 *position)
-{
- // Calculate camera
- if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position);
-
- return internalCamera;
-}
-
//----------------------------------------------------------------------------------
// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
//----------------------------------------------------------------------------------
@@ -915,8 +795,8 @@ int GetMouseWheelMove(void)
{
return previousMouseWheelY;
}
-#endif
+// Hide mouse cursor
void HideCursor()
{
#if defined(PLATFORM_DESKTOP)
@@ -936,6 +816,7 @@ void HideCursor()
cursorHidden = true;
}
+// Show mouse cursor
void ShowCursor()
{
#if defined(PLATFORM_DESKTOP)
@@ -948,10 +829,12 @@ void ShowCursor()
cursorHidden = false;
}
+// Check if mouse cursor is hidden
bool IsCursorHidden()
{
return cursorHidden;
}
+#endif
// TODO: Enable gamepad usage on Rapsberry Pi
// NOTE: emscripten not implemented
@@ -1105,6 +988,18 @@ void SetPostproShader(Shader shader)
}
}
+// Set custom shader to be used in batch draw
+void SetCustomShader(Shader shader)
+{
+ rlglSetCustomShader(shader);
+}
+
+// Set default shader to be used in batch draw
+void SetDefaultShader(void)
+{
+ rlglSetDefaultShader();
+}
+
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
@@ -2161,260 +2056,3 @@ static void LogoAnimation(void)
showLogo = false; // Prevent for repeating when reloading window (Android)
}
-
-// Process desired camera mode and controls
-static void ProcessCamera(Camera *camera, Vector3 *playerPosition)
-{
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI)
- // Mouse movement detection
- if (cameraMode != CAMERA_FREE)
- {
- HideCursor();
- if (GetMousePosition().x < GetScreenHeight() / 3) SetMousePosition((Vector2){ screenWidth - GetScreenHeight() / 3, GetMousePosition().y});
- else if (GetMousePosition().y < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - GetScreenHeight() / 3});
- else if (GetMousePosition().x > screenWidth - GetScreenHeight() / 3) SetMousePosition((Vector2) { GetScreenHeight() / 3, GetMousePosition().y});
- else if (GetMousePosition().y > screenHeight - GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() / 3});
- else
- {
- cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x;
- cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y;
- }
- }
- else
- {
- ShowCursor();
- cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x;
- cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y;
- }
-
- cameraMousePosition = GetMousePosition();
-
- // Support for multiple automatic camera modes
- switch (cameraMode)
- {
- case CAMERA_FREE:
- {
- // Pass to orbiting camera
- if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL;
-
- // Camera zoom
- if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
- {
- cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
-
- if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP;
- }
- // Camera looking down
- else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
- {
- camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- }
- else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0))
- {
- camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
-
- if (camera->target.y < 0) camera->target.y = -0.001;
- }
- else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0))
- {
- cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
- if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
- }
- // Camera looking up
- else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0))
- {
- camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- }
- else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0))
- {
- camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
- camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance;
-
- if (camera->target.y > 0) camera->target.y = 0.001;
- }
- else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0))
- {
- cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
- if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
- }
-
-
- // Inputs
- if (IsKeyDown(KEY_LEFT_ALT))
- {
- if (IsKeyDown(KEY_LEFT_CONTROL))
- {
- // Camera smooth zoom
- if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY);
- }
- // Camera orientation calculation
- else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON))
- {
- // Camera orientation calculation
- // Get the mouse sensitivity
- cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY;
- cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY;
-
- // Angle clamp
- if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD;
- else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD;
- }
- }
- // Paning
- else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON))
- {
- camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
- camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
- camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER);
- }
-
- // Focus to center
- if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 };
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
-
- } break;
- case CAMERA_ORBITAL:
- {
- // Pass to free camera
- if (IsKeyPressed('O')) cameraMode = CAMERA_FREE;
-
- cameraAngle.x += ORBITAL_CAMERA_SPEED;
-
- // Camera zoom
- cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
- // Camera distance clamp
- if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
-
- // Focus to center
- if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 };
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
-
- } break;
- case CAMERA_FIRST_PERSON:
- case CAMERA_THIRD_PERSON:
- {
- bool isMoving = false;
-
- // Keyboard inputs
- if (IsKeyDown('W'))
- {
- playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
- else if (IsKeyDown('S'))
- {
- playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
-
- if (IsKeyDown('A'))
- {
- playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
- else if (IsKeyDown('D'))
- {
- playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
-
- if (IsKeyDown('E'))
- {
- if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER;
- }
- else if (IsKeyDown('Q'))
- {
- if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER;
- }
-
- if (cameraMode == CAMERA_THIRD_PERSON)
- {
- // Camera orientation calculation
- // Get the mouse sensitivity
- cameraAngle.x += cameraMouseVariation.x * -THIRD_PERSON_MOUSE_SENSITIVITY;
- cameraAngle.y += cameraMouseVariation.y * -THIRD_PERSON_MOUSE_SENSITIVITY;
-
- // Angle clamp
- if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD;
- else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD;
-
- // Camera zoom
- cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY);
-
- // Camera distance clamp
- if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
-
- // Camera is always looking at player
- camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x);
- camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y;
- camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x);
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z;
- }
- else
- {
- if (isMoving) cameraMovementCounter++;
-
- // Camera orientation calculation
- // Get the mouse sensitivity
- cameraAngle.x += cameraMouseVariation.x * -FIRST_PERSON_MOUSE_SENSITIVITY;
- cameraAngle.y += cameraMouseVariation.y * -FIRST_PERSON_MOUSE_SENSITIVITY;
-
- // Angle clamp
- if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD;
- else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD;
-
- // Camera is always looking at player
- camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE;
- camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE;
- camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE;
-
- camera->position.x = playerPosition->x;
- camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER;
- camera->position.z = playerPosition->z;
-
- camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER;
- camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER;
- }
- } break;
- default: break;
- }
-#endif
-} \ No newline at end of file
diff --git a/src/gestures.c b/src/gestures.c
index 00b57e4c..2574aa02 100644
--- a/src/gestures.c
+++ b/src/gestures.c
@@ -30,9 +30,14 @@
#include <stdlib.h> // malloc(), free()
#include <stdio.h> // printf(), fprintf()
#include <math.h> // Used for ...
-#include <time.h> // Used for clock functions
#include <stdint.h> // Defines int32_t, int64_t
+#if defined(_WIN32)
+ //#include <Windows.h>
+#elif defined(__linux)
+ #include <time.h> // Used for clock functions
+#endif
+
#if defined(PLATFORM_ANDROID)
#include <jni.h> // Java native interface
#include <android/sensor.h> // Android sensors functions
@@ -75,15 +80,10 @@ typedef struct {
// Global Variables Definition
//----------------------------------------------------------------------------------
-// typedef
static GestureType gestureType = TYPE_MOTIONLESS;
-
-// Gestures detection variables
+static double eventTime = 0;
//static int32_t touchId; // Not used...
-// Event
-static int64_t eventTime = 0;
-
// Tap
// Our initial press position on tap
static Vector2 initialTapPosition = { 0, 0 };
@@ -127,7 +127,7 @@ static float pinchDelta = 0;
// Detected gesture
static int currentGesture = GESTURE_NONE;
-static float touchX, touchY;
+static Vector2 touchPosition;
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
@@ -141,7 +141,7 @@ static float OnPinch();
static void SetDualInput(GestureEvent event);
static float Distance(Vector2 v1, Vector2 v2);
static float DotProduct(Vector2 v1, Vector2 v2);
-static int GetCurrentTime();
+static double GetCurrentTime();
#if defined(PLATFORM_WEB)
static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
@@ -158,9 +158,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// Returns tap position XY
extern Vector2 GetRawPosition(void)
{
- Vector2 position = { touchX, touchY };
-
- return position;
+ return touchPosition;
}
// Check if a gesture have been detected
@@ -531,13 +529,27 @@ static float DotProduct(Vector2 v1, Vector2 v2)
return result;
}
-static int GetCurrentTime()
+static double GetCurrentTime()
{
+#if defined(_WIN32)
+/*
+ // NOTE: Requires Windows.h
+ FILETIME tm;
+ GetSystemTimePreciseAsFileTime(&tm);
+ ULONGLONG nowTime = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; // Time provided in 100-nanosecond intervals
+
+ return ((double)nowTime/10000000.0); // Return time in seconds
+*/
+#endif
+
+#if defined(__linux)
+ // NOTE: Only for Linux-based systems
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time provided in nanoseconds
- return nowTime / 1000000; // Return time in miliseconds
+ return ((double)nowTime/1000000.0); // Return time in miliseconds
+#endif
}
#if defined(PLATFORM_ANDROID)
@@ -545,23 +557,21 @@ static int GetCurrentTime()
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
int type = AInputEvent_getType(event);
- //int32_t key = 0;
if (type == AINPUT_EVENT_TYPE_MOTION)
{
- touchX = AMotionEvent_getX(event, 0);
- touchY = AMotionEvent_getY(event, 0);
+ touchPosition.x = AMotionEvent_getX(event, 0);
+ touchPosition.y = AMotionEvent_getY(event, 0);
}
else if (type == AINPUT_EVENT_TYPE_KEY)
{
- //key = AKeyEvent_getKeyCode(event);
+ //int32_t key = AKeyEvent_getKeyCode(event);
//int32_t AKeyEvent_getMetaState(event);
}
int32_t action = AMotionEvent_getAction(event);
unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
-
GestureEvent gestureEvent;
// Action
@@ -609,6 +619,8 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent
}
*/
+ touchPosition = gestureEvent.position[0];
+
GestureEvent gestureEvent;
// Action
diff --git a/src/models.c b/src/models.c
index ffb077d4..be1b949a 100644
--- a/src/models.c
+++ b/src/models.c
@@ -599,6 +599,8 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
int mapX = heightmap.width;
int mapZ = heightmap.height;
+
+ Color *heightmapPixels = GetPixelData(heightmap);
// NOTE: One vertex per pixel
// TODO: Consider resolution when generating model data?
@@ -628,15 +630,15 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
// one triangle - 3 vertex
vData.vertices[vCounter] = x;
- vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor;
+ vData.vertices[vCounter + 1] = GetHeightValue(heightmapPixels[x + z*mapX])*scaleFactor;
vData.vertices[vCounter + 2] = z;
vData.vertices[vCounter + 3] = x;
- vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor;
+ vData.vertices[vCounter + 4] = GetHeightValue(heightmapPixels[x + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 5] = z+1;
vData.vertices[vCounter + 6] = x+1;
- vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor;
+ vData.vertices[vCounter + 7] = GetHeightValue(heightmapPixels[(x+1) + z*mapX])*scaleFactor;
vData.vertices[vCounter + 8] = z;
// another triangle - 3 vertex
@@ -649,7 +651,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
vData.vertices[vCounter + 14] = vData.vertices[vCounter + 5];
vData.vertices[vCounter + 15] = x+1;
- vData.vertices[vCounter + 16] = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor;
+ vData.vertices[vCounter + 16] = GetHeightValue(heightmapPixels[(x+1) + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 17] = z+1;
vCounter += 18; // 6 vertex, 18 floats
@@ -691,6 +693,8 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
trisCounter += 2;
}
}
+
+ free(heightmapPixels);
// Fill color data
// NOTE: Not used any more... just one plain color defined at DrawModel()
@@ -713,17 +717,19 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
}
// Load a map image as a 3d model (cubes based)
-Model LoadCubicmap(Image cubesmap)
+Model LoadCubicmap(Image cubicmap)
{
VertexData vData;
+ Color *cubicmapPixels = GetPixelData(cubicmap);
+
// Map cube size will be 1.0
float mapCubeSide = 1.0f;
- int mapWidth = cubesmap.width * (int)mapCubeSide;
- int mapHeight = cubesmap.height * (int)mapCubeSide;
+ int mapWidth = cubicmap.width * (int)mapCubeSide;
+ int mapHeight = cubicmap.height * (int)mapCubeSide;
// NOTE: Max possible number of triangles numCubes * (12 triangles by cube)
- int maxTriangles = cubesmap.width*cubesmap.height*12;
+ int maxTriangles = cubicmap.width*cubicmap.height*12;
int vCounter = 0; // Used to count vertices
int tcCounter = 0; // Used to count texcoords
@@ -775,9 +781,9 @@ Model LoadCubicmap(Image cubesmap)
Vector3 v8 = { x + w/2, 0, z + h/2 };
// We check pixel color to be WHITE, we will full cubes
- if ((cubesmap.pixels[z*cubesmap.width + x].r == 255) &&
- (cubesmap.pixels[z*cubesmap.width + x].g == 255) &&
- (cubesmap.pixels[z*cubesmap.width + x].b == 255))
+ if ((cubicmapPixels[z*cubicmap.width + x].r == 255) &&
+ (cubicmapPixels[z*cubicmap.width + x].g == 255) &&
+ (cubicmapPixels[z*cubicmap.width + x].b == 255))
{
// Define triangles (Checking Collateral Cubes!)
//----------------------------------------------
@@ -832,10 +838,10 @@ Model LoadCubicmap(Image cubesmap)
mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
tcCounter += 6;
- if (((z < cubesmap.height - 1) &&
- (cubesmap.pixels[(z + 1)*cubesmap.width + x].r == 0) &&
- (cubesmap.pixels[(z + 1)*cubesmap.width + x].g == 0) &&
- (cubesmap.pixels[(z + 1)*cubesmap.width + x].b == 0)) || (z == cubesmap.height - 1))
+ if (((z < cubicmap.height - 1) &&
+ (cubicmapPixels[(z + 1)*cubicmap.width + x].r == 0) &&
+ (cubicmapPixels[(z + 1)*cubicmap.width + x].g == 0) &&
+ (cubicmapPixels[(z + 1)*cubicmap.width + x].b == 0)) || (z == cubicmap.height - 1))
{
// Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
// NOTE: Collateral occluded faces are not generated
@@ -865,9 +871,9 @@ Model LoadCubicmap(Image cubesmap)
}
if (((z > 0) &&
- (cubesmap.pixels[(z - 1)*cubesmap.width + x].r == 0) &&
- (cubesmap.pixels[(z - 1)*cubesmap.width + x].g == 0) &&
- (cubesmap.pixels[(z - 1)*cubesmap.width + x].b == 0)) || (z == 0))
+ (cubicmapPixels[(z - 1)*cubicmap.width + x].r == 0) &&
+ (cubicmapPixels[(z - 1)*cubicmap.width + x].g == 0) &&
+ (cubicmapPixels[(z - 1)*cubicmap.width + x].b == 0)) || (z == 0))
{
// Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
// NOTE: Collateral occluded faces are not generated
@@ -896,10 +902,10 @@ Model LoadCubicmap(Image cubesmap)
tcCounter += 6;
}
- if (((x < cubesmap.width - 1) &&
- (cubesmap.pixels[z*cubesmap.width + (x + 1)].r == 0) &&
- (cubesmap.pixels[z*cubesmap.width + (x + 1)].g == 0) &&
- (cubesmap.pixels[z*cubesmap.width + (x + 1)].b == 0)) || (x == cubesmap.width - 1))
+ if (((x < cubicmap.width - 1) &&
+ (cubicmapPixels[z*cubicmap.width + (x + 1)].r == 0) &&
+ (cubicmapPixels[z*cubicmap.width + (x + 1)].g == 0) &&
+ (cubicmapPixels[z*cubicmap.width + (x + 1)].b == 0)) || (x == cubicmap.width - 1))
{
// Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
// NOTE: Collateral occluded faces are not generated
@@ -929,9 +935,9 @@ Model LoadCubicmap(Image cubesmap)
}
if (((x > 0) &&
- (cubesmap.pixels[z*cubesmap.width + (x - 1)].r == 0) &&
- (cubesmap.pixels[z*cubesmap.width + (x - 1)].g == 0) &&
- (cubesmap.pixels[z*cubesmap.width + (x - 1)].b == 0)) || (x == 0))
+ (cubicmapPixels[z*cubicmap.width + (x - 1)].r == 0) &&
+ (cubicmapPixels[z*cubicmap.width + (x - 1)].g == 0) &&
+ (cubicmapPixels[z*cubicmap.width + (x - 1)].b == 0)) || (x == 0))
{
// Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
// NOTE: Collateral occluded faces are not generated
@@ -961,9 +967,9 @@ Model LoadCubicmap(Image cubesmap)
}
}
// We check pixel color to be BLACK, we will only draw floor and roof
- else if ((cubesmap.pixels[z*cubesmap.width + x].r == 0) &&
- (cubesmap.pixels[z*cubesmap.width + x].g == 0) &&
- (cubesmap.pixels[z*cubesmap.width + x].b == 0))
+ else if ((cubicmapPixels[z*cubicmap.width + x].r == 0) &&
+ (cubicmapPixels[z*cubicmap.width + x].g == 0) &&
+ (cubicmapPixels[z*cubicmap.width + x].b == 0))
{
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
mapVertices[vCounter] = v1;
@@ -1065,6 +1071,8 @@ Model LoadCubicmap(Image cubesmap)
free(mapVertices);
free(mapNormals);
free(mapTexcoords);
+
+ free(cubicmapPixels);
// NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
@@ -1335,6 +1343,8 @@ bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSph
// NOTE: player position (or camera) is modified inside this function
Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius)
{
+ Color *cubicmapPixels = GetPixelData(cubicmap);
+
// Detect the cell where the player is located
Vector3 impactDirection = { 0, 0, 0 };
@@ -1347,8 +1357,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
// Multiple Axis --------------------------------------------------------------------------------------------
// Axis x-, y-
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
@@ -1360,8 +1370,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x-, y+
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
@@ -1373,8 +1383,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x+, y-
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
@@ -1386,8 +1396,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x+, y+
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
@@ -1401,7 +1411,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
// Single Axis ---------------------------------------------------------------------------------------------------
// Axis x-
- if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0)
+ if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0)
{
if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius)
{
@@ -1410,7 +1420,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
}
// Axis x+
- if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0)
+ if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0)
{
if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius)
{
@@ -1419,7 +1429,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
}
// Axis y-
- if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)
+ if (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)
{
if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)
{
@@ -1428,7 +1438,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
}
// Axis y+
- if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)
+ if (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)
{
if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)
{
@@ -1440,9 +1450,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
// Diagonals -------------------------------------------------------------------------------------------------------
// Axis x-, y-
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
@@ -1460,9 +1470,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x-, y+
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
@@ -1480,9 +1490,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x+, y-
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) &&
+ (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
@@ -1500,9 +1510,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
}
// Axis x+, y+
- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0))
+ if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) &&
+ (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0))
{
if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
@@ -1531,6 +1541,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
playerPosition->y = (1.5 - radius) - 0.01;
impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z};
}
+
+ free(cubicmapPixels);
return impactDirection;
}
diff --git a/src/raylib.h b/src/raylib.h
index e804da62..bd1692d2 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -27,7 +27,6 @@
*
* Some design decisions:
* 32bit Colors - All defined color are always RGBA
-* 32bit Textures - All loaded images are converted automatically to RGBA textures
* SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures
* One custom default font is loaded automatically when InitWindow()
* If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined)
@@ -78,7 +77,7 @@
// Some basic Defines
//----------------------------------------------------------------------------------
#ifndef PI
-#define PI 3.14159265358979323846
+ #define PI 3.14159265358979323846
#endif
#define DEG2RAD (PI / 180.0f)
@@ -183,20 +182,6 @@
typedef enum { false, true } bool;
#endif
-typedef enum {
- GESTURE_NONE = 0,
- GESTURE_TAP,
- GESTURE_DOUBLETAP,
- GESTURE_HOLD,
- GESTURE_DRAG,
- GESTURE_SWIPE_RIGHT,
- GESTURE_SWIPE_LEFT,
- GESTURE_SWIPE_UP,
- GESTURE_SWIPE_DOWN,
- GESTURE_PINCH_IN,
- GESTURE_PINCH_OUT
-} Gestures;
-
// byte type
typedef unsigned char byte;
@@ -240,17 +225,21 @@ typedef struct Rectangle {
// Image type, bpp always RGBA (32bit)
// NOTE: Data stored in CPU memory (RAM)
typedef struct Image {
- Color *pixels;
- int width;
- int height;
+ void *data; // Image raw data
+ int width; // Image base width
+ int height; // Image base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (TextureFormat)
} Image;
// Texture2D type, bpp always RGBA (32bit)
// NOTE: Data stored in GPU memory
typedef struct Texture2D {
- unsigned int id; // OpenGL id
- int width;
- int height;
+ unsigned int id; // OpenGL texture id
+ int width; // Texture base width
+ int height; // Texture base height
+ int mipmaps; // Mipmap levels, 1 by default
+ int format; // Data format (TextureFormat)
} Texture2D;
// Character type (one font glyph)
@@ -337,9 +326,11 @@ typedef struct Wave {
short channels;
} Wave;
-// Texture formats (support depends on OpenGL version)
+// Texture formats
+// NOTE: Support depends on OpenGL version and platform
typedef enum {
UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
+ UNCOMPRESSED_GRAY_ALPHA, // 16 bpp (2 channels)
UNCOMPRESSED_R5G6B5, // 16 bpp
UNCOMPRESSED_R8G8B8, // 24 bpp
UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha)
@@ -354,9 +345,25 @@ typedef enum {
COMPRESSED_ETC2_EAC_RGBA, // 8 bpp
COMPRESSED_PVRT_RGB, // 4 bpp
COMPRESSED_PVRT_RGBA, // 4 bpp
- /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp
+ COMPRESSED_ASTC_4x4_RGBA, // 8 bpp
+ COMPRESSED_ASTC_8x8_RGBA // 2 bpp
} TextureFormat;
+// Gestures type
+typedef enum {
+ GESTURE_NONE = 0,
+ GESTURE_TAP,
+ GESTURE_DOUBLETAP,
+ GESTURE_HOLD,
+ GESTURE_DRAG,
+ GESTURE_SWIPE_RIGHT,
+ GESTURE_SWIPE_LEFT,
+ GESTURE_SWIPE_UP,
+ GESTURE_SWIPE_DOWN,
+ GESTURE_PINCH_IN,
+ GESTURE_PINCH_OUT
+} Gestures;
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@@ -402,16 +409,27 @@ int GetHexValue(Color color); // Returns hexadecim
int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
-void SetCameraMode(int mode); // Multiple camera modes available
-Camera UpdateCamera(Vector3 *position); // Update camera with position (when using internal camera)
-
void SetConfigFlags(char flags); // Enable some window configurations
void ShowLogo(void); // Activates raylib logo at startup (can be done with flags)
void SetPostproShader(Shader shader); // Set fullscreen postproduction shader
+void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw
+void SetDefaultShader(void); // Set default shader to be used in batch draw
Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Gives the rayTrace from mouse position
+// Camera modes setup and control functions (module: camera)
+void SetCameraMode(int mode); // Select camera mode (multiple camera modes available)
+Camera UpdateCamera(Vector3 *position); // Update camera with position
+
+void SetCameraControls(int front, int left, int back, int right, int up, int down);
+void SetMouseSensitivity(float sensitivity);
+void SetResetPosition(Vector3 resetPosition);
+void SetResetControl(int resetKey);
+void SetPawnControl(int pawnControlKey);
+void SetFnControl(int fnControlKey);
+void SetSmoothZoomControl(int smoothZoomControlKey);
+
//------------------------------------------------------------------------------------
// Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------
@@ -451,6 +469,7 @@ int GetTouchX(void); // Returns touch positio
int GetTouchY(void); // Returns touch position Y
Vector2 GetTouchPosition(void); // Returns touch position XY
+// Gestures System (module: gestures)
bool IsGestureDetected(void);
int GetGestureType(void);
float GetDragIntensity(void);
@@ -504,6 +523,8 @@ Texture2D CreateTexture(Image image, bool genMipmaps);
void UnloadImage(Image image); // Unload image from CPU memory (RAM)
void UnloadTexture(Texture2D texture); // Unload texture from GPU memory
void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two)
+Color *GetPixelData(Image image); // Get pixel data from image as a Color struct array
+void SetPixelData(Image *image, Color *pixels, int format); // Set image data from Color struct array
void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2
diff --git a/src/rlgl.c b/src/rlgl.c
index a5c40815..fbea84a2 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -143,6 +143,7 @@ typedef struct {
typedef struct {
GLuint textureId;
int vertexCount;
+ // TODO: DrawState state -> Blending mode, shader
} DrawCall;
// pixel type (same as Color type)
@@ -175,6 +176,7 @@ static VertexPositionColorTextureIndexBuffer quads;
// Shader Programs
static Shader defaultShader, simpleShader;
+static Shader currentShader; // By default, defaultShader
// Vertex Array Objects (VAO)
static GLuint vaoLines, vaoTriangles, vaoQuads;
@@ -330,14 +332,14 @@ void rlRotatef(float angleDeg, float x, float y, float z)
Matrix rotation = MatrixIdentity();
// OPTION 1: It works...
- //if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD);
- //else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD);
- //else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD);
+ if (x == 1) rotation = MatrixRotateX(angleDeg*DEG2RAD);
+ else if (y == 1) rotation = MatrixRotateY(angleDeg*DEG2RAD);
+ else if (z == 1) rotation = MatrixRotateZ(angleDeg*DEG2RAD);
// OPTION 2: Requires review...
- Vector3 axis = (Vector3){ x, y, z };
- VectorNormalize(&axis);
- rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD);
+ //Vector3 axis = (Vector3){ x, y, z };
+ //VectorNormalize(&axis);
+ //rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD);
// OPTION 3: TODO: Review, it doesn't work!
//Vector3 vec = (Vector3){ x, y, z };
@@ -921,6 +923,8 @@ void rlglInit(void)
defaultShader = LoadDefaultShader();
simpleShader = LoadSimpleShader();
//customShader = rlglLoadShader("custom.vs", "custom.fs"); // Works ok
+
+ currentShader = defaultShader;
InitializeBuffers(); // Init vertex arrays
InitializeBuffersGPU(); // Init VBO and VAO
@@ -1094,6 +1098,7 @@ void rlglClose(void)
#endif
}
+// Drawing batches: triangles, quads, lines
void rlglDraw(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
@@ -1101,11 +1106,11 @@ void rlglDraw(void)
if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0))
{
- glUseProgram(defaultShader.id);
+ glUseProgram(currentShader.id);
- glUniformMatrix4fv(defaultShader.projectionLoc, 1, false, GetMatrixVector(projection));
- glUniformMatrix4fv(defaultShader.modelviewLoc, 1, false, GetMatrixVector(modelview));
- glUniform1i(defaultShader.textureLoc, 0);
+ glUniformMatrix4fv(currentShader.projectionLoc, 1, false, GetMatrixVector(projection));
+ glUniformMatrix4fv(currentShader.modelviewLoc, 1, false, GetMatrixVector(modelview));
+ glUniform1i(currentShader.textureLoc, 0);
}
// NOTE: We draw in this order: triangle shapes, textured quads and lines
@@ -1121,12 +1126,12 @@ void rlglDraw(void)
else
{
glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(defaultShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
}
glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter);
@@ -1149,16 +1154,16 @@ void rlglDraw(void)
{
// Enable vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
- glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(defaultShader.texcoordLoc);
+ glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.texcoordLoc);
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(defaultShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
}
@@ -1206,12 +1211,12 @@ void rlglDraw(void)
else
{
glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(defaultShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
}
glDrawArrays(GL_LINES, 0, lines.vCounter);
@@ -1310,7 +1315,7 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r
// NOTE: Drawing in OpenGL 3.3+, transform is passed to shader
glUniformMatrix4fv(model.shader.projectionLoc, 1, false, GetMatrixVector(projection));
glUniformMatrix4fv(model.shader.modelviewLoc, 1, false, GetMatrixVector(modelviewworld));
- glUniform1i(model.shader.textureLoc, 0);
+ glUniform1i(model.shader.textureLoc, 0); // Texture fits in texture unit 0 (Check glActiveTexture())
// Apply color tinting to model
// NOTE: Just update one uniform on fragment shader
@@ -1573,16 +1578,18 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma
GLuint id;
- // TODO: Review compressed textures support by OpenGL version
- /* (rlGetVersion() == OPENGL_11)
- if ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA) ||
- (textureFormat == COMPRESSED_ETC1_RGB8) || (textureFormat == COMPRESSED_ETC2_RGB8) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA8))
+ // Check compressed textures support by OpenGL 1.1
+ if (rlGetVersion() == OPENGL_11)
{
- id = 0;
- TraceLog(WARNING, "GPU compressed textures not supported on OpenGL 1.1");
- return id;
+ if ((textureFormat == COMPRESSED_ETC1_RGB) || (textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA) ||
+ (textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA) ||
+ (textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA))
+ {
+ id = 0;
+ TraceLog(WARNING, "Required GPU compressed texture format not supported");
+ return id;
+ }
}
- */
glGenTextures(1, &id); // Generate Pointer to the texture
@@ -1679,10 +1686,17 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma
// With swizzleMask we define how a one channel texture will be mapped to RGBA
// Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle
- GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, 1.0f };
+ GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
- TraceLog(INFO, "Grayscale texture loaded and swizzled!");
+ TraceLog(INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", id);
+ } break;
+ case UNCOMPRESSED_GRAY_ALPHA:
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data);
+
+ GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
} break;
case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break;
case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
@@ -1711,6 +1725,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma
switch (textureFormat)
{
case UNCOMPRESSED_GRAYSCALE: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
+ case UNCOMPRESSED_GRAY_ALPHA: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break;
case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break;
@@ -1724,7 +1739,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma
case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
//case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
- default: TraceLog(WARNING, "Texture format not recognized"); break;
+ default: TraceLog(WARNING, "Texture format not supported"); break;
}
if ((mipmapCount == 1) && (genMipmaps))
@@ -2009,13 +2024,50 @@ void rlglSetModelShader(Model *model, Shader shader)
#endif
}
+// Set custom shader to be used on batch draw
+void rlglSetCustomShader(Shader shader)
+{
+ if (currentShader.id != shader.id)
+ {
+ rlglDraw();
+
+ currentShader = shader;
+/*
+ if (vaoSupported) glBindVertexArray(vaoQuads);
+
+ // Enable vertex attributes: position
+ glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+
+ // Enable vertex attributes: texcoords
+ glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
+ glEnableVertexAttribArray(currentShader.texcoordLoc);
+ glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+
+ // Enable vertex attributes: colors
+ glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+
+ if (vaoSupported) glBindVertexArray(0); // Unbind VAO
+*/
+ }
+}
+
+// Set default shader to be used on batch draw
+void rlglSetDefaultShader(void)
+{
+ rlglSetCustomShader(defaultShader);
+}
+
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-void PrintProjectionMatrix()
+void PrintProjectionMatrix(void)
{
PrintMatrix(projection);
}
-void PrintModelviewMatrix()
+void PrintModelviewMatrix(void)
{
PrintMatrix(modelview);
}
@@ -2292,7 +2344,7 @@ static void InitializeBuffers(void)
}
// Initialize Vertex Array Objects (Contain VBO)
-// NOTE: lines, triangles and quads buffers use defaultShader
+// NOTE: lines, triangles and quads buffers use currentShader
static void InitializeBuffersGPU(void)
{
if (vaoSupported)
@@ -2308,14 +2360,14 @@ static void InitializeBuffersGPU(void)
// Lines - Vertex positions buffer binding and attributes enable
glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
// Lines - colors buffer
glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.colorLoc);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines);
else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]);
@@ -2334,13 +2386,13 @@ static void InitializeBuffersGPU(void)
// Enable vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.colorLoc);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles);
else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]);
@@ -2359,18 +2411,18 @@ static void InitializeBuffersGPU(void)
// Enable vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.vertexLoc);
- glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.texcoordLoc);
- glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.texcoordLoc);
+ glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW);
- glEnableVertexAttribArray(defaultShader.colorLoc);
- glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
// Fill index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
diff --git a/src/rlgl.h b/src/rlgl.h
index 3335fe16..41c13ef7 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -216,6 +216,8 @@ void rlglInitPostpro(void); // Initialize postprocessing sys
void rlglDrawPostpro(void); // Draw with postprocessing shader
void rlglSetPostproShader(Shader shader); // Set postprocessing shader
void rlglSetModelShader(Model *model, Shader shader); // Set shader for a model
+void rlglSetCustomShader(Shader shader); // Set custom shader to be used on batch draw
+void rlglSetDefaultShader(void); // Set default shader to be used on batch draw
Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids
void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires);
diff --git a/src/text.c b/src/text.c
index 7b3fc7d2..8e3fc4b9 100644
--- a/src/text.c
+++ b/src/text.c
@@ -87,6 +87,8 @@ extern void LoadDefaultFont(void)
Image image;
image.width = 128; // We know our default font image is 128 pixels width
image.height = 128; // We know our default font image is 128 pixels height
+ image.mipmaps = 1;
+ image.format = UNCOMPRESSED_R8G8B8A8;
// Default font is directly defined here (data generated from a sprite font image)
// This way, we reconstruct SpriteFont without creating large global variables
@@ -149,9 +151,9 @@ extern void LoadDefaultFont(void)
// Re-construct image from defaultFontData and generate OpenGL texture
//----------------------------------------------------------------------
- image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color));
+ Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color));
- for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array
+ for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array
int counter = 0; // Font data elements counter
@@ -160,7 +162,7 @@ extern void LoadDefaultFont(void)
{
for (int j = 31; j >= 0; j--)
{
- if (BIT_CHECK(defaultFontData[counter], j)) image.pixels[i+j] = WHITE;
+ if (BIT_CHECK(defaultFontData[counter], j)) imagePixels[i+j] = WHITE;
}
counter++;
@@ -171,6 +173,10 @@ extern void LoadDefaultFont(void)
//FILE *myimage = fopen("default_font.raw", "wb");
//fwrite(image.pixels, 1, 128*128*4, myimage);
//fclose(myimage);
+
+ SetPixelData(&image, imagePixels, 0);
+
+ free(imagePixels);
defaultFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture
UnloadImage(image);
@@ -232,14 +238,16 @@ SpriteFont LoadSpriteFont(const char *fileName)
{
Image image = LoadImage(fileName);
- // At this point we have a pixel array with all the data...
+ // At this point we have a data array...
+
+ Color *imagePixels = GetPixelData(image);
#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
ConvertToPOT(&image, MAGENTA);
#endif
// Process bitmap Font pixel data to get measures (Character array)
// spriteFont.charSet data is filled inside the function and memory is allocated!
- int numChars = ParseImageData(image.pixels, image.width, image.height, &spriteFont.charSet);
+ int numChars = ParseImageData(imagePixels, image.width, image.height, &spriteFont.charSet);
TraceLog(INFO, "[%s] SpriteFont data parsed correctly", fileName);
TraceLog(INFO, "[%s] SpriteFont num chars detected: %i", fileName, numChars);
@@ -248,6 +256,7 @@ SpriteFont LoadSpriteFont(const char *fileName)
spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture
+ free(imagePixels);
UnloadImage(image);
}
@@ -522,6 +531,8 @@ static SpriteFont LoadRBMF(const char *fileName)
image.width = (int)rbmfHeader.imgWidth;
image.height = (int)rbmfHeader.imgHeight;
+ image.mipmaps = 1;
+ image.format = UNCOMPRESSED_R8G8B8A8;
int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32;
@@ -535,9 +546,9 @@ static SpriteFont LoadRBMF(const char *fileName)
// Re-construct image from rbmfFileData
//-----------------------------------------
- image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color));
+ Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color));
- for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array
+ for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array
int counter = 0; // Font data elements counter
@@ -546,11 +557,15 @@ static SpriteFont LoadRBMF(const char *fileName)
{
for (int j = 31; j >= 0; j--)
{
- if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE;
+ if (BIT_CHECK(rbmfFileData[counter], j)) imagePixels[i+j] = WHITE;
}
counter++;
}
+
+ SetPixelData(&image, imagePixels, 0);
+
+ free(imagePixels);
TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
@@ -607,7 +622,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
Image image;
image.width = 512;
image.height = 512;
- image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color));
+ //image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color));
unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25);
@@ -647,7 +662,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
free(ttfBuffer);
// Now we have image data in tempBitmap and chardata filled...
-
+/*
for (int i = 0; i < 512*512; i++)
{
image.pixels[i].r = tempBitmap[i];
@@ -655,7 +670,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
image.pixels[i].b = tempBitmap[i];
image.pixels[i].a = 255;
}
-
+*/
free(tempBitmap);
// REFERENCE EXAMPLE
diff --git a/src/textures.c b/src/textures.c
index 1d22e509..d2b2de1d 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -47,13 +47,7 @@
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-typedef struct {
- unsigned char *data; // Image raw data
- int width; // Image base width
- int height; // Image base height
- int mipmaps; // Mipmap levels, 1 by default
- int format; // Data format
-} ImageEx;
+// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
@@ -63,14 +57,16 @@ typedef struct {
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by text)
//----------------------------------------------------------------------------------
-//...
+// ...
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
-static ImageEx LoadDDS(const char *fileName); // Load DDS file
-static ImageEx LoadPKM(const char *fileName); // Load PKM file
-static ImageEx LoadPVR(const char *fileName); // Load PVR file
+static Image LoadDDS(const char *fileName); // Load DDS file
+static Image LoadPKM(const char *fileName); // Load PKM file
+static Image LoadKTX(const char *fileName); // Load KTX file
+static Image LoadPVR(const char *fileName); // Load PVR file
+static Image LoadASTC(const char *fileName); // Load ASTC file
//----------------------------------------------------------------------------------
// Module Functions Definition
@@ -81,10 +77,12 @@ Image LoadImage(const char *fileName)
{
Image image;
- // Initial values
- image.pixels = NULL;
+ // Initialize image default values
+ image.data = NULL;
image.width = 0;
image.height = 0;
+ image.mipmaps = 0;
+ image.format = 0;
if ((strcmp(GetExtension(fileName),"png") == 0) ||
(strcmp(GetExtension(fileName),"bmp") == 0) ||
@@ -94,82 +92,39 @@ Image LoadImage(const char *fileName)
(strcmp(GetExtension(fileName),"psd") == 0) ||
(strcmp(GetExtension(fileName),"pic") == 0))
{
- int imgWidth;
- int imgHeight;
- int imgBpp;
-
+ int imgWidth = 0;
+ int imgHeight = 0;
+ int imgBpp = 0;
+
// NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
- // Force loading to 4 components (RGBA)
- byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);
-
- if (imgData != NULL)
- {
- // Convert array to pixel array for working convenience
- image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color));
-
- int pix = 0;
-
- for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4)
- {
- image.pixels[pix].r = imgData[i];
- image.pixels[pix].g = imgData[i+1];
- image.pixels[pix].b = imgData[i+2];
- image.pixels[pix].a = imgData[i+3];
- pix++;
- }
-
- stbi_image_free(imgData);
-
- image.width = imgWidth;
- image.height = imgHeight;
-
- TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
- }
- else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
- }
- else if (strcmp(GetExtension(fileName),"dds") == 0)
- {
- // NOTE: DDS uncompressed images can also be loaded (discarding mipmaps...)
-
- ImageEx imageDDS = LoadDDS(fileName);
+ image.data = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 0);
- if (imageDDS.format == 0)
- {
- image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color));
- image.width = imageDDS.width;
- image.height = imageDDS.height;
-
- int pix = 0;
-
- for (int i = 0; i < (image.width * image.height * 4); i += 4)
- {
- image.pixels[pix].r = imageDDS.data[i];
- image.pixels[pix].g = imageDDS.data[i+1];
- image.pixels[pix].b = imageDDS.data[i+2];
- image.pixels[pix].a = imageDDS.data[i+3];
- pix++;
- }
-
- free(imageDDS.data);
-
- TraceLog(INFO, "[%s] DDS Image loaded successfully (uncompressed, no mipmaps)", fileName);
- }
- else TraceLog(WARNING, "[%s] DDS Compressed image data could not be loaded", fileName);
+ image.width = imgWidth;
+ image.height = imgHeight;
+ image.mipmaps = 1;
+
+ if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE;
+ else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA;
+ else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8;
+ else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8;
}
- else if (strcmp(GetExtension(fileName),"pkm") == 0)
- {
- TraceLog(INFO, "[%s] PKM Compressed image data could not be loaded", fileName);
+ else if (strcmp(GetExtension(fileName),"dds") == 0) image = LoadDDS(fileName);
+ else if (strcmp(GetExtension(fileName),"pkm") == 0) image = LoadPKM(fileName);
+ else if (strcmp(GetExtension(fileName),"ktx") == 0) image = LoadKTX(fileName);
+ else if (strcmp(GetExtension(fileName),"pvr") == 0) image = LoadPVR(fileName);
+ else if (strcmp(GetExtension(fileName),"astc") == 0) image = LoadASTC(fileName);
+
+ if (image.data != NULL)
+ {
+ TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
}
- else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName);
-
- // ALTERNATIVE: We can load pixel data directly into Color struct pixels array,
- // to do that, struct data alignment should be the right one (4 byte); it is.
- //image.pixels = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);
+ else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
return image;
}
// Load an image from rRES file (raylib Resource)
+// TODO: Review function to support multiple color modes
Image LoadImageFromRES(const char *rresName, int resId)
{
Image image;
@@ -232,28 +187,19 @@ Image LoadImageFromRES(const char *rresName, int resId)
image.width = (int)imgWidth;
image.height = (int)imgHeight;
- unsigned char *data = malloc(infoHeader.size);
+ unsigned char *compData = malloc(infoHeader.size);
- fread(data, infoHeader.size, 1, rresFile);
+ fread(compData, infoHeader.size, 1, rresFile);
- unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize);
+ unsigned char *imgData = DecompressData(compData, infoHeader.size, infoHeader.srcSize);
- image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight);
+ // TODO: Review color mode
+ //image.data = (unsigned char *)malloc(sizeof(unsigned char)*imgWidth*imgHeight*4);
+ image.data = imgData;
- int pix = 0;
+ //free(imgData);
- for (int i = 0; i < (imgWidth*imgHeight*4); i += 4)
- {
- image.pixels[pix].r = imgData[i];
- image.pixels[pix].g = imgData[i+1];
- image.pixels[pix].b = imgData[i+2];
- image.pixels[pix].a = imgData[i+3];
- pix++;
- }
-
- free(imgData);
-
- free(data);
+ free(compData);
TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height);
}
@@ -294,66 +240,14 @@ Texture2D LoadTexture(const char *fileName)
{
Texture2D texture;
- // Init texture to default values
- texture.id = 0;
- texture.width = 0;
- texture.height = 0;
-
- if (strcmp(GetExtension(fileName),"dds") == 0)
- {
- ImageEx image = LoadDDS(fileName);
-
- texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false);
-
- texture.width = image.width;
- texture.height = image.height;
-
- if (texture.id == 0) TraceLog(WARNING, "[%s] DDS texture could not be loaded", fileName);
- else TraceLog(INFO, "[%s] DDS texture loaded successfully", fileName);
-
- free(image.data);
- }
- else if (strcmp(GetExtension(fileName),"pkm") == 0)
- {
- ImageEx image = LoadPKM(fileName);
-
- texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false);
-
- texture.width = image.width;
- texture.height = image.height;
-
- if (texture.id == 0) TraceLog(WARNING, "[%s] PKM texture could not be loaded", fileName);
- else TraceLog(INFO, "[%s] PKM texture loaded successfully", fileName);
-
- free(image.data);
- }
- else if (strcmp(GetExtension(fileName),"pvr") == 0)
- {
- ImageEx image = LoadPVR(fileName);
-
- texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false);
-
- texture.width = image.width;
- texture.height = image.height;
-
- if (texture.id == 0) TraceLog(WARNING, "[%s] PVR texture could not be loaded", fileName);
- else TraceLog(INFO, "[%s] PVR texture loaded successfully", fileName);
-
- free(image.data);
- }
- else
- {
- Image image = LoadImage(fileName);
-
- if (image.pixels != NULL)
- {
+ Image image = LoadImage(fileName);
+
#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
- ConvertToPOT(&image, BLANK);
+ ConvertToPOT(&image, BLANK);
#endif
- texture = LoadTextureFromImage(image, false);
- UnloadImage(image);
- }
- }
+
+ texture = LoadTextureFromImage(image, false);
+ UnloadImage(image);
return texture;
}
@@ -364,6 +258,8 @@ Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, in
texture.width = width;
texture.height = height;
+ texture.mipmaps = mipmapCount;
+ texture.format = textureFormat;
texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount, genMipmaps);
@@ -380,45 +276,15 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps)
texture.id = 0;
texture.width = 0;
texture.height = 0;
-
- if ((image.pixels != NULL) && (image.width > 0) && (image.height > 0))
- {
- unsigned char *imgData = malloc(image.width * image.height * 4);
-
- int j = 0;
-
- for (int i = 0; i < image.width * image.height * 4; i += 4)
- {
- imgData[i] = image.pixels[j].r;
- imgData[i+1] = image.pixels[j].g;
- imgData[i+2] = image.pixels[j].b;
- imgData[i+3] = image.pixels[j].a;
-
- j++;
- }
-
- // NOTE: rlglLoadTexture() can generate mipmaps (POT image required)
- texture.id = rlglLoadTexture(imgData, image.width, image.height, UNCOMPRESSED_R8G8B8A8, 1, genMipmaps);
-
- texture.width = image.width;
- texture.height = image.height;
-
- free(imgData);
- }
- else TraceLog(WARNING, "Texture could not be loaded, image data is not valid");
-
- return texture;
-}
-
-// [DEPRECATED] Load a texture from image data
-// NOTE: Use LoadTextureFromImage() instead
-Texture2D CreateTexture(Image image, bool genMipmaps)
-{
- Texture2D texture;
-
- texture = LoadTextureFromImage(image, genMipmaps);
-
- TraceLog(INFO, "Created texture id: %i", texture.id);
+ texture.mipmaps = 0;
+ texture.format = 0;
+
+ texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false);
+
+ texture.width = image.width;
+ texture.height = image.height;
+ texture.mipmaps = image.mipmaps;
+ texture.format = image.format;
return texture;
}
@@ -438,7 +304,7 @@ Texture2D LoadTextureFromRES(const char *rresName, int resId)
// Unload image from CPU memory (RAM)
void UnloadImage(Image image)
{
- free(image.pixels);
+ free(image.data);
}
// Unload texture from GPU memory
@@ -451,6 +317,8 @@ void UnloadTexture(Texture2D texture)
// NOTE: Requirement on OpenGL ES 2.0 (RPI, HTML5)
void ConvertToPOT(Image *image, Color fillColor)
{
+ // TODO: Review for new image struct
+ /*
// Just add the required amount of pixels at the right and bottom sides of image...
int potWidth = GetNextPOT(image->width);
int potHeight = GetNextPOT(image->height);
@@ -467,7 +335,7 @@ void ConvertToPOT(Image *image, Color fillColor)
{
for (int i = 0; i < potWidth; i++)
{
- if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->pixels[j*image->width + i];
+ if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->data[j*image->width + i];
else imgDataPixelPOT[j*potWidth + i] = fillColor;
}
}
@@ -480,6 +348,115 @@ void ConvertToPOT(Image *image, Color fillColor)
image->width = potWidth;
image->height = potHeight;
}
+ */
+}
+
+// Get pixel data from image in the form of Color struct array
+Color *GetPixelData(Image image)
+{
+ Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color));
+
+ int k = 0;
+
+ for (int i = 0; i < image.width*image.height; i++)
+ {
+ switch (image.format)
+ {
+ case UNCOMPRESSED_GRAYSCALE:
+ {
+ pixels[i].r = ((unsigned char *)image.data)[k];
+ pixels[i].g = ((unsigned char *)image.data)[k];
+ pixels[i].b = ((unsigned char *)image.data)[k];
+ pixels[i].a = 255;
+
+ k++;
+ } break;
+ case UNCOMPRESSED_GRAY_ALPHA:
+ {
+ pixels[i].r = ((unsigned char *)image.data)[k];
+ pixels[i].g = ((unsigned char *)image.data)[k];
+ pixels[i].b = ((unsigned char *)image.data)[k];
+ pixels[i].a = ((unsigned char *)image.data)[k + 1];
+
+ k += 2;
+ } break;
+ case UNCOMPRESSED_R5G5B5A1:
+ {
+ unsigned short pixel = ((unsigned short *)image.data)[k];
+
+ pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
+ pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
+ pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
+ pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
+
+ k++;
+ } break;
+ case UNCOMPRESSED_R5G6B5:
+ {
+ unsigned short pixel = ((unsigned short *)image.data)[k];
+
+ pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
+ pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
+ pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
+ pixels[i].a = 255;
+
+ k++;
+ } break;
+ case UNCOMPRESSED_R4G4B4A4:
+ {
+ unsigned short pixel = ((unsigned short *)image.data)[k];
+
+ pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
+ pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
+ pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
+ pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
+
+ k++;
+ } break;
+ case UNCOMPRESSED_R8G8B8A8:
+ {
+ pixels[i].r = ((unsigned char *)image.data)[k];
+ pixels[i].g = ((unsigned char *)image.data)[k + 1];
+ pixels[i].b = ((unsigned char *)image.data)[k + 2];
+ pixels[i].a = ((unsigned char *)image.data)[k + 3];
+
+ k += 4;
+ } break;
+ case UNCOMPRESSED_R8G8B8:
+ {
+ pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
+ pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
+ pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
+ pixels[i].a = 255;
+
+ k += 3;
+ } break;
+ default: TraceLog(WARNING, "Format not supported for pixel data retrieval"); break;
+ }
+ }
+
+ return pixels;
+}
+
+// Fill image data with pixels Color data (RGBA - 32bit)
+// NOTE: Pixels color array size must be coherent with image size
+// TODO: Review to support different color modes (TextureFormat)
+void SetPixelData(Image *image, Color *pixels, int format)
+{
+ free(image->data);
+ image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char));
+
+ int k = 0;
+
+ for (int i = 0; i < image->width*image->height*4; i += 4)
+ {
+ ((unsigned char *)image->data)[i] = pixels[k].r;
+ ((unsigned char *)image->data)[i + 1] = pixels[k].g;
+ ((unsigned char *)image->data)[i + 2] = pixels[k].b;
+ ((unsigned char *)image->data)[i + 3] = pixels[k].a;
+
+ k++;
+ }
}
// Draw a Texture2D
@@ -554,9 +531,17 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
//----------------------------------------------------------------------------------
// Loading DDS image data (compressed or uncompressed)
-// NOTE: Compressed data loading not supported on OpenGL 1.1
-static ImageEx LoadDDS(const char *fileName)
+static Image LoadDDS(const char *fileName)
{
+ // Required extension:
+ // GL_EXT_texture_compression_s3tc
+
+ // Supported tokens (defined by extensions)
+ // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+ // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+ // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+ // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
@@ -569,7 +554,7 @@ static ImageEx LoadDDS(const char *fileName)
unsigned int rgbBitCount;
unsigned int rBitMask;
unsigned int gBitMask;
- unsigned int bitMask;
+ unsigned int bBitMask;
unsigned int aBitMask;
} ddsPixelFormat;
@@ -581,7 +566,7 @@ static ImageEx LoadDDS(const char *fileName)
unsigned int width;
unsigned int pitchOrLinearSize;
unsigned int depth;
- unsigned int mipMapCount;
+ unsigned int mipmapCount;
unsigned int reserved1[11];
ddsPixelFormat ddspf;
unsigned int caps;
@@ -591,8 +576,7 @@ static ImageEx LoadDDS(const char *fileName)
unsigned int reserved2;
} ddsHeader;
- ImageEx image;
- ddsHeader header;
+ Image image;
image.data = NULL;
image.width = 0;
@@ -604,7 +588,7 @@ static ImageEx LoadDDS(const char *fileName)
if (ddsFile == NULL)
{
- TraceLog(WARNING, "[%s] DDS image file could not be opened", fileName);
+ TraceLog(WARNING, "[%s] DDS file could not be opened", fileName);
}
else
{
@@ -616,11 +600,12 @@ static ImageEx LoadDDS(const char *fileName)
if (strncmp(filecode, "DDS ", 4) != 0)
{
TraceLog(WARNING, "[%s] DDS file does not seem to be a valid image", fileName);
- fclose(ddsFile);
}
else
{
- // Get the surface descriptor
+ ddsHeader header;
+
+ // Get the image header
fread(&header, sizeof(ddsHeader), 1, ddsFile);
TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader));
@@ -630,63 +615,85 @@ static ImageEx LoadDDS(const char *fileName)
image.width = header.width;
image.height = header.height;
+ image.mipmaps = 1; // Default value, could be changed (header.mipmapCount)
- if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
+ if (header.ddspf.rgbBitCount == 16) // 16bit mode, no compressed
{
- image.data = (unsigned char *)malloc(header.width * header.height * 4);
- unsigned char *buffer = (unsigned char *)malloc(header.width * header.height * 3);
-
- fread(buffer, image.width*image.height*3, 1, ddsFile);
-
- unsigned char *src = buffer;
- unsigned char *dest = image.data;
+ if (header.ddspf.flags == 0x40) // no alpha channel
+ {
+ image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short));
+ fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile);
- for(int y = 0; y < image.height; y++)
+ image.format = UNCOMPRESSED_R5G6B5;
+ }
+ else if (header.ddspf.flags == 0x41) // with alpha channel
{
- for(int x = 0; x < image.width; x++)
+ if (header.ddspf.aBitMask == 0x8000) // 1bit alpha
+ {
+ image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short));
+ fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile);
+
+ unsigned char alpha = 0;
+
+ // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
+ for (int i = 0; i < image.width*image.height; i++)
+ {
+ alpha = ((unsigned short *)image.data)[i] >> 15;
+ ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 1;
+ ((unsigned short *)image.data)[i] += alpha;
+ }
+
+ image.format = UNCOMPRESSED_R5G5B5A1;
+ }
+ else if (header.ddspf.aBitMask == 0xf000) // 4bit alpha
{
- *dest++ = *src++;
- *dest++ = *src++;
- *dest++ = *src++;
- *dest++ = 255;
+ image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short));
+ fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile);
+
+ unsigned char alpha = 0;
+
+ // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
+ for (int i = 0; i < image.width*image.height; i++)
+ {
+ alpha = ((unsigned short *)image.data)[i] >> 12;
+ ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4;
+ ((unsigned short *)image.data)[i] += alpha;
+ }
+
+ image.format = UNCOMPRESSED_R4G4B4A4;
}
}
+ }
+ if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
+ {
+ // NOTE: not sure if this case exists...
+ image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char));
+ fread(image.data, image.width*image.height*3, 1, ddsFile);
- free(buffer);
-
- image.mipmaps = 1;
image.format = UNCOMPRESSED_R8G8B8;
}
else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
{
- image.data = (unsigned char *)malloc(header.width * header.height * 4);
-
+ image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char));
fread(image.data, image.width*image.height*4, 1, ddsFile);
- image.mipmaps = 1;
image.format = UNCOMPRESSED_R8G8B8A8;
}
else if (((header.ddspf.flags == 0x04) || (header.ddspf.flags == 0x05)) && (header.ddspf.fourCC > 0))
{
- //TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName);
- //TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName);
-
int bufsize;
// Calculate data size, including all mipmaps
- if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2;
+ if (header.mipmapCount > 1) bufsize = header.pitchOrLinearSize*2;
else bufsize = header.pitchOrLinearSize;
TraceLog(DEBUG, "Pitch or linear size: %i", header.pitchOrLinearSize);
- image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
+ image.data = (unsigned char*)malloc(bufsize*sizeof(unsigned char));
fread(image.data, 1, bufsize, ddsFile);
- // Close file pointer
- fclose(ddsFile);
-
- image.mipmaps = header.mipMapCount;
+ image.mipmaps = header.mipmapCount;
switch(header.ddspf.fourCC)
{
@@ -701,6 +708,8 @@ static ImageEx LoadDDS(const char *fileName)
}
}
}
+
+ fclose(ddsFile); // Close file pointer
}
return image;
@@ -709,94 +718,195 @@ static ImageEx LoadDDS(const char *fileName)
// Loading PKM image data (ETC1/ETC2 compression)
// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
-// ETC1 compression support requires extension GL_OES_compressed_ETC1_RGB8_texture
-static ImageEx LoadPKM(const char *fileName)
+static Image LoadPKM(const char *fileName)
{
- // If OpenGL ES 2.0. the following format could be supported (ETC1):
- //GL_ETC1_RGB8_OES
-
- // If OpenGL ES 3.0, the following formats are supported (ETC2/EAC):
- //GL_COMPRESSED_RGB8_ETC2
- //GL_COMPRESSED_RGBA8_ETC2
- //GL_COMPRESSED_RG11_EAC
- //...
+ // Required extensions:
+ // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0)
+ // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0)
+
+ // Supported tokens (defined by extensions)
+ // GL_ETC1_RGB8_OES 0x8D64
+ // GL_COMPRESSED_RGB8_ETC2 0x9274
+ // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
// PKM file (ETC1) Header (16 bytes)
typedef struct {
char id[4]; // "PKM "
- char version[2]; // "10"
- unsigned short format; // Format = number of mipmaps = 0 (ETC1_RGB_NO_MIPMAPS)
- unsigned short extWidth; // Texture width (big-endian)
- unsigned short extHeight; // Texture height (big-endian)
+ char version[2]; // "10" or "20"
+ unsigned short format; // Data format (big-endian) (Check list below)
+ unsigned short width; // Texture width (big-endian) (origWidth rounded to multiple of 4)
+ unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4)
unsigned short origWidth; // Original width (big-endian)
unsigned short origHeight; // Original height (big-endian)
} pkmHeader;
+
+ // Formats list
+ // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
+ // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
// NOTE: The extended width and height are the widths rounded up to a multiple of 4.
- // NOTE: ETC is always 4bit per pixel (64 bits for each 4x4 block of pixels)
+ // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
- // Bytes Swap (little-endian <-> big-endian)
- //unsigned short data;
- //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8);
-
- ImageEx image;
-
- unsigned short width;
- unsigned short height;
- unsigned short useless;
+ Image image;
+
+ image.data = NULL;
+ image.width = 0;
+ image.height = 0;
+ image.mipmaps = 0;
+ image.format = 0;
FILE *pkmFile = fopen(fileName, "rb");
if (pkmFile == NULL)
{
- TraceLog(WARNING, "[%s] PKM image file could not be opened", fileName);
+ TraceLog(WARNING, "[%s] PKM file could not be opened", fileName);
}
else
{
- // Verify the type of file
- char filecode[4];
+ pkmHeader header;
- fread(filecode, 1, 4, pkmFile);
+ // Get the image header
+ fread(&header, sizeof(pkmHeader), 1, pkmFile);
- if (strncmp(filecode, "PKM ", 4) != 0)
+ if (strncmp(header.id, "PKM ", 4) != 0)
{
TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName);
- fclose(pkmFile);
}
else
{
- // Get the surface descriptor
- fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard version
- fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard format
-
- fread(&width, sizeof(unsigned short), 1, pkmFile); // Read extended width
- fread(&height, sizeof(unsigned short), 1, pkmFile); // Read extended height
-
- int size = (width/4)*(height/4)*8; // Total data size in bytes
+ // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
+ header.format = ((header.format & 0x00FF) << 8) | ((header.format & 0xFF00) >> 8);
+ header.width = ((header.width & 0x00FF) << 8) | ((header.width & 0xFF00) >> 8);
+ header.height = ((header.height & 0x00FF) << 8) | ((header.height & 0xFF00) >> 8);
+
+ TraceLog(INFO, "PKM (ETC) image width: %i", header.width);
+ TraceLog(INFO, "PKM (ETC) image height: %i", header.height);
+ TraceLog(INFO, "PKM (ETC) image format: %i", header.format);
+
+ image.width = header.width;
+ image.height = header.height;
+ image.mipmaps = 1;
+
+ int size = image.width*image.height*4/8; // Total data size in bytes
image.data = (unsigned char*)malloc(size * sizeof(unsigned char));
fread(image.data, 1, size, pkmFile);
- fclose(pkmFile); // Close file pointer
-
- image.width = width;
- image.height = height;
- image.mipmaps = 1;
- image.format = COMPRESSED_ETC1_RGB;
+ if (header.format == 0) image.format = COMPRESSED_ETC1_RGB;
+ else if (header.format == 1) image.format = COMPRESSED_ETC2_RGB;
+ else if (header.format == 3) image.format = COMPRESSED_ETC2_EAC_RGBA;
}
+
+ fclose(pkmFile); // Close file pointer
+ }
+
+ return image;
+}
+
+// Load KTX compressed image data (ETC1/ETC2 compression)
+static Image LoadKTX(const char *fileName)
+{
+ // Required extensions:
+ // GL_OES_compressed_ETC1_RGB8_texture (ETC1)
+ // GL_ARB_ES3_compatibility (ETC2/EAC)
+
+ // Supported tokens (defined by extensions)
+ // GL_ETC1_RGB8_OES 0x8D64
+ // GL_COMPRESSED_RGB8_ETC2 0x9274
+ // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+
+ // KTX file Header (64 bytes)
+ // https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+ typedef struct {
+ char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
+ unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
+ unsigned int glType; // For compressed textures, glType must equal 0
+ unsigned int glTypeSize; // For compressed texture data, usually 1
+ unsigned int glFormat; // For compressed textures is 0
+ unsigned int glInternalFormat; // Compressed internal format
+ unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...)
+ unsigned int width; // Texture image width in pixels
+ unsigned int height; // Texture image height in pixels
+ unsigned int depth; // For 2D textures is 0
+ unsigned int elements; // Number of array elements, usually 0
+ unsigned int faces; // Cubemap faces, for no-cubemap = 1
+ unsigned int mipmapLevels; // Non-mipmapped textures = 1
+ unsigned int keyValueDataSize; // Used to encode any arbitrary data...
+ } ktxHeader;
+
+ // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
+
+ Image image;
+
+ image.width = 0;
+ image.height = 0;
+ image.mipmaps = 0;
+ image.format = 0;
+
+ FILE *ktxFile = fopen(fileName, "rb");
+
+ if (ktxFile == NULL)
+ {
+ TraceLog(WARNING, "[%s] KTX image file could not be opened", fileName);
}
+ else
+ {
+ ktxHeader header;
+
+ // Get the image header
+ fread(&header, sizeof(ktxHeader), 1, ktxFile);
+
+ if ((header.id[1] != 'K') || (header.id[2] != 'T') || (header.id[3] != 'X') ||
+ (header.id[4] != ' ') || (header.id[5] != '1') || (header.id[6] != '1'))
+ {
+ TraceLog(WARNING, "[%s] KTX file does not seem to be a valid file", fileName);
+ }
+ else
+ {
+ image.width = header.width;
+ image.height = header.height;
+ image.mipmaps = header.mipmapLevels;
+
+ TraceLog(INFO, "KTX (ETC) image width: %i", header.width);
+ TraceLog(INFO, "KTX (ETC) image height: %i", header.height);
+ TraceLog(INFO, "KTX (ETC) image format: 0x%x", header.glInternalFormat);
+
+ unsigned char unused;
+
+ if (header.keyValueDataSize > 0)
+ {
+ for (int i = 0; i < header.keyValueDataSize; i++) fread(&unused, 1, 1, ktxFile);
+ }
+
+ int dataSize;
+ fread(&dataSize, sizeof(unsigned int), 1, ktxFile);
+
+ image.data = (unsigned char*)malloc(dataSize * sizeof(unsigned char));
+ fread(image.data, 1, dataSize, ktxFile);
+
+ if (header.glInternalFormat == 0x8D64) image.format = COMPRESSED_ETC1_RGB;
+ else if (header.glInternalFormat == 0x9274) image.format = COMPRESSED_ETC2_RGB;
+ else if (header.glInternalFormat == 0x9278) image.format = COMPRESSED_ETC2_EAC_RGBA;
+ }
+
+ fclose(ktxFile); // Close file pointer
+ }
+
return image;
}
// Loading PVR image data (uncompressed or PVRT compression)
-// NOTE: PVR compression requires extension GL_IMG_texture_compression_pvrtc (PowerVR GPUs)
-static ImageEx LoadPVR(const char *fileName)
+// NOTE: PVR v2 not supported, use PVR v3 instead
+static Image LoadPVR(const char *fileName)
{
- // If supported in OpenGL ES 2.0. the following formats could be defined:
- // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB)
- // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGBA)
+ // Required extension:
+ // GL_IMG_texture_compression_pvrtc
+
+ // Supported tokens (defined by extensions)
+ // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+ // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
// PVR file v2 Header (52 bytes)
typedef struct {
@@ -818,25 +928,30 @@ static ImageEx LoadPVR(const char *fileName)
// PVR file v3 Header (52 bytes)
// NOTE: After it could be metadata (15 bytes?)
typedef struct {
- unsigned int version;
- unsigned int flags;
- unsigned long long pixelFormat;
- unsigned int colourSpace;
- unsigned int channelType;
- unsigned int height;
- unsigned int width;
- unsigned int depth;
- unsigned int numSurfaces;
- unsigned int numFaces;
- unsigned int numMipmaps;
- unsigned int metaDataSize;
+ char id[4];
+ unsigned int flags;
+ unsigned char channels[4]; // pixelFormat high part
+ unsigned char channelDepth[4]; // pixelFormat low part
+ unsigned int colourSpace;
+ unsigned int channelType;
+ unsigned int height;
+ unsigned int width;
+ unsigned int depth;
+ unsigned int numSurfaces;
+ unsigned int numFaces;
+ unsigned int numMipmaps;
+ unsigned int metaDataSize;
} pvrHeaderV3;
- // Bytes Swap (little-endian <-> big-endian)
- //unsigned short data;
- //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8);
+ // Metadata (usually 15 bytes)
+ typedef struct {
+ unsigned int devFOURCC;
+ unsigned int key;
+ unsigned int dataSize; // Not used?
+ unsigned char *data; // Not used?
+ } pvrMetadata;
- ImageEx image;
+ Image image;
image.data = NULL;
image.width = 0;
@@ -848,48 +963,164 @@ static ImageEx LoadPVR(const char *fileName)
if (pvrFile == NULL)
{
- TraceLog(WARNING, "[%s] PVR image file could not be opened", fileName);
+ TraceLog(WARNING, "[%s] PVR file could not be opened", fileName);
}
else
{
// Check PVR image version
- unsigned int pvrVersion = 0;
- fread(&pvrVersion, sizeof(unsigned int), 1, pvrFile);
- fsetpos(pvrFile, 0);
+ unsigned char pvrVersion = 0;
+ fread(&pvrVersion, sizeof(unsigned char), 1, pvrFile);
+ fseek(pvrFile, 0, SEEK_SET);
- if (pvrVersion == 52)
- {
- pvrHeaderV2 header;
-
- fread(&header, sizeof(pvrHeaderV2), 1, pvrFile);
-
- image.width = header.width;
- image.height = header.height;
- image.mipmaps = header.numMipmaps;
- image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA
- }
- else if (pvrVersion == 3)
+ // Load different PVR data formats
+ if (pvrVersion == 0x50)
{
pvrHeaderV3 header;
+ // Get PVR image header
fread(&header, sizeof(pvrHeaderV3), 1, pvrFile);
- image.width = header.width;
- image.height = header.height;
- image.mipmaps = header.numMipmaps;
-
- // TODO: Skip metaDataSize
-
- image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA
+ if ((header.id[0] != 'P') || (header.id[1] != 'V') || (header.id[2] != 'R') || (header.id[3] != 3))
+ {
+ TraceLog(WARNING, "[%s] PVR file does not seem to be a valid image", fileName);
+ }
+ else
+ {
+ image.width = header.width;
+ image.height = header.height;
+ image.mipmaps = header.numMipmaps + 1;
+
+ // Check data format
+ if (((header.channels[0] == 'l') && (header.channels[1] == 0)) && (header.channelDepth[0] == 8)) image.format = UNCOMPRESSED_GRAYSCALE;
+ else if (((header.channels[0] == 'l') && (header.channels[1] == 'a')) && ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8))) image.format = UNCOMPRESSED_GRAY_ALPHA;
+ else if ((header.channels[0] == 'r') && (header.channels[1] == 'g') && (header.channels[2] == 'b'))
+ {
+ if (header.channels[3] == 'a')
+ {
+ if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 5) && (header.channelDepth[2] == 5) && (header.channelDepth[3] == 1)) image.format = UNCOMPRESSED_R5G5B5A1;
+ else if ((header.channelDepth[0] == 4) && (header.channelDepth[1] == 4) && (header.channelDepth[2] == 4) && (header.channelDepth[3] == 4)) image.format = UNCOMPRESSED_R4G4B4A4;
+ else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8) && (header.channelDepth[3] == 8)) image.format = UNCOMPRESSED_R8G8B8A8;
+ }
+ else if (header.channels[3] == 0)
+ {
+ if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 6) && (header.channelDepth[2] == 5)) image.format = UNCOMPRESSED_R5G6B5;
+ else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8)) image.format = UNCOMPRESSED_R8G8B8;
+ }
+ }
+ else if (header.channels[0] == 2) image.format = COMPRESSED_PVRT_RGB;
+ else if (header.channels[0] == 3) image.format = COMPRESSED_PVRT_RGBA;
+
+ // Skip meta data header
+ unsigned char unused = 0;
+ for (int i = 0; i < header.metaDataSize; i++) fread(&unused, sizeof(unsigned char), 1, pvrFile);
+
+ // Calculate data size (depends on format)
+ int bpp = 0;
+
+ switch (image.format)
+ {
+ case UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
+ case UNCOMPRESSED_GRAY_ALPHA:
+ case UNCOMPRESSED_R5G5B5A1:
+ case UNCOMPRESSED_R5G6B5:
+ case UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
+ case UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
+ case UNCOMPRESSED_R8G8B8: bpp = 24; break;
+ case COMPRESSED_PVRT_RGB:
+ case COMPRESSED_PVRT_RGBA: bpp = 4; break;
+ default: break;
+ }
+
+ int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
+ image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
+
+ // Read data from file
+ fread(image.data, dataSize, 1, pvrFile);
+ }
}
+ else if (pvrVersion == 52) TraceLog(INFO, "PVR v2 not supported, update your files to PVR v3");
- int size = (image.width/4)*(image.height/4)*8; // Total data size in bytes
+ fclose(pvrFile); // Close file pointer
+ }
- image.data = (unsigned char*)malloc(size * sizeof(unsigned char));
+ return image;
+}
- fread(image.data, 1, size, pvrFile);
+// Load ASTC compressed image data (ASTC compression)
+static Image LoadASTC(const char *fileName)
+{
+ // Required extensions:
+ // GL_KHR_texture_compression_astc_hdr
+ // GL_KHR_texture_compression_astc_ldr
+
+ // Supported tokens (defined by extensions)
+ // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0
+ // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7
+
+ // ASTC file Header (16 bytes)
+ typedef struct {
+ unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C
+ unsigned char blockX; // Block X dimensions
+ unsigned char blockY; // Block Y dimensions
+ unsigned char blockZ; // Block Z dimensions (1 for 2D images)
+ unsigned char width[3]; // Image width in pixels (24bit value)
+ unsigned char height[3]; // Image height in pixels (24bit value)
+ unsigned char lenght[3]; // Image Z-size (1 for 2D images)
+ } astcHeader;
- fclose(pvrFile); // Close file pointer
+ Image image;
+
+ image.data = NULL;
+ image.width = 0;
+ image.height = 0;
+ image.mipmaps = 0;
+ image.format = 0;
+
+ FILE *astcFile = fopen(fileName, "rb");
+
+ if (astcFile == NULL)
+ {
+ TraceLog(WARNING, "[%s] ASTC file could not be opened", fileName);
+ }
+ else
+ {
+ astcHeader header;
+
+ // Get ASTC image header
+ fread(&header, sizeof(astcHeader), 1, astcFile);
+
+ if ((header.id[3] != 0x5c) || (header.id[2] != 0xa1) || (header.id[1] != 0xab) || (header.id[0] != 0x13))
+ {
+ TraceLog(WARNING, "[%s] ASTC file does not seem to be a valid image", fileName);
+ }
+ else
+ {
+ image.width = 0x00000000 | ((int)header.width[0] << 16) | ((int)header.width[1] << 8) | ((int)header.width[2]);
+ image.height = 0x00000000 | ((int)header.height[0] << 16) | ((int)header.height[1] << 8) | ((int)header.height[2]);
+ image.mipmaps = 1;
+
+ TraceLog(DEBUG, "ASTC image width: %i", image.width);
+ TraceLog(DEBUG, "ASTC image height: %i", image.height);
+ TraceLog(DEBUG, "ASTC image blocks: %ix%i", header.blockX, header.blockY);
+
+ // NOTE: Each block is always stored in 128bit so we can calculate the bpp
+ int bpp = 128/(header.blockX*header.blockY);
+
+ // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
+ if ((bpp == 8) || (bpp == 2))
+ {
+ int dataSize = image.width*image.height*bpp/8; // Data size in bytes
+
+ image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
+ fread(image.data, dataSize, 1, astcFile);
+
+ if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA;
+ else if (bpp == 2) image.format = COMPRESSED_ASTC_4x4_RGBA;
+ }
+ else TraceLog(WARNING, "[%s] ASTC block size configuration not supported", fileName);
+ }
+
+ fclose(astcFile);
}
return image;