diff --git a/src/core.c b/src/core.c index 384e043a..cc8f31fd 100644 --- a/src/core.c +++ b/src/core.c @@ -180,9 +180,9 @@ static int currentMouseWheelY = 0; // Required to track mouse wheel var static int exitKey = KEY_ESCAPE; // Default exit key (ESC) static int lastKeyPressed = -1; -#endif static bool cursorHidden; +#endif static double currentTime, previousTime; // Used to track timmings static double updateTime, drawTime; // Time measures for update and draw @@ -227,6 +227,7 @@ static void SwapBuffers(void); // Copy back buffer to f static void PollInputEvents(void); // Register user events static void LogoAnimation(void); // Plays raylib logo appearing animation static void SetupFramebufferSize(int displayWidth, int displayHeight); + #if defined(PLATFORM_RPI) static void InitMouse(void); // Mouse initialization (including mouse thread) static void *MouseThread(void *arg); // Mouse reading thread diff --git a/src/makefile b/src/makefile index 77aa2b5d..1908b138 100644 --- a/src/makefile +++ b/src/makefile @@ -93,7 +93,7 @@ else endif # define all object files required -OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o +OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o camera.o gestures.o # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is raylib @@ -148,6 +148,14 @@ utils.o: utils.c stb_vorbis.o: stb_vorbis.c $(CC) -c stb_vorbis.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) +# compile camera module +camera.o: camera.c + $(CC) -c camera.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile gestures module +gestures.o: gestures.c + $(CC) -c gestures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + # clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) diff --git a/src/raylib.h b/src/raylib.h index bd1692d2..0eafa390 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -515,16 +515,15 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 //------------------------------------------------------------------------------------ Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) +Image LoadImageFromData(Color *pixels, int width, int height, int format); // Load image from Color array data Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load a texture from raw data into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) -Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() 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/raymath.c b/src/raymath.c index 9763b075..f5e30833 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -431,71 +431,77 @@ Matrix MatrixSubstract(Matrix left, Matrix right) } // Returns translation matrix -// TODO: Review this function Matrix MatrixTranslate(float x, float y, float z) { -/* - For OpenGL - 1, 0, 0, 0 - 0, 1, 0, 0 - 0, 0, 1, 0 - x, y, z, 1 - Is the correct Translation Matrix. Why? Opengl Uses column-major matrix ordering. - Which is the Transpose of the Matrix you initially presented, which is in row-major ordering. - Row major is used in most math text-books and also DirectX, so it is a common - point of confusion for those new to OpenGL. - - * matrix notation used in opengl documentation does not describe in-memory layout for OpenGL matrices - - Translation matrix should be laid out in memory like this: - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, trabsX, transY, transZ, 1 } - - - 9.005 Are OpenGL matrices column-major or row-major? - - For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out - contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements - of the 16-element matrix, where indices are numbered from 1 to 16 as described in section - 2.11.2 of the OpenGL 2.1 Specification. - - Column-major versus row-major is purely a notational convention. Note that post-multiplying - with column-major matrices produces the same result as pre-multiplying with row-major matrices. - The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. - You can use any notation, as long as it's clearly stated. - - Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion - in the OpenGL programming community. Column-major notation suggests that matrices - are not laid out in memory as a programmer would expect. -*/ - Matrix result = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }; return result; } -// Returns rotation matrix -// TODO: Review this function -Matrix MatrixRotate(float angleX, float angleY, float angleZ) +// Create rotation matrix from axis and angle +// NOTE: Angle should be provided in radians +Matrix MatrixRotate(float angle, Vector3 axis) { Matrix result; - Matrix rotX = MatrixRotateX(angleX); - Matrix rotY = MatrixRotateY(angleY); - Matrix rotZ = MatrixRotateZ(angleZ); + Matrix mat = MatrixIdentity(); - result = MatrixMultiply(MatrixMultiply(rotX, rotY), rotZ); + float x = axis.x, y = axis.y, z = axis.z; + + float length = sqrt(x*x + y*y + z*z); + + if ((length != 1) && (length != 0)) + { + length = 1/length; + x *= length; + y *= length; + z *= length; + } + + float s = sinf(angle); + float c = cosf(angle); + float t = 1.0f - c; + + // Cache some matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + + // Construct the elements of the rotation matrix + float b00 = x*x*t + c, b01 = y*x*t + z*s, b02 = z*x*t - y*s; + float b10 = x*y*t - z*s, b11 = y*y*t + c, b12 = z*y*t + x*s; + float b20 = x*z*t + y*s, b21 = y*z*t - x*s, b22 = z*z*t + c; + + // Perform rotation-specific matrix multiplication + result.m0 = a00*b00 + a10*b01 + a20*b02; + result.m1 = a01*b00 + a11*b01 + a21*b02; + result.m2 = a02*b00 + a12*b01 + a22*b02; + result.m3 = a03*b00 + a13*b01 + a23*b02; + result.m4 = a00*b10 + a10*b11 + a20*b12; + result.m5 = a01*b10 + a11*b11 + a21*b12; + result.m6 = a02*b10 + a12*b11 + a22*b12; + result.m7 = a03*b10 + a13*b11 + a23*b12; + result.m8 = a00*b20 + a10*b21 + a20*b22; + result.m9 = a01*b20 + a11*b21 + a21*b22; + result.m10 = a02*b20 + a12*b21 + a22*b22; + result.m11 = a03*b20 + a13*b21 + a23*b22; + result.m12 = mat.m12; + result.m13 = mat.m13; + result.m14 = mat.m14; + result.m15 = mat.m15; return result; } /* +// Another implementation for MatrixRotate... Matrix MatrixRotate(float angle, float x, float y, float z) { Matrix result = MatrixIdentity(); - float c = cosf(angle*DEG2RAD); // cosine - float s = sinf(angle*DEG2RAD); // sine - float c1 = 1.0f - c; // 1 - c + float c = cosf(angle); // cosine + float s = sinf(angle); // sine + float c1 = 1.0f - c; // 1 - c float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12, m1 = result.m1, m5 = result.m5, m9 = result.m9, m13 = result.m13, @@ -530,124 +536,6 @@ Matrix MatrixRotate(float angle, float x, float y, float z) } */ -// Create rotation matrix from axis and angle -// TODO: Test this function -// NOTE: NO prototype defined! -Matrix MatrixFromAxisAngle(Vector3 axis, float angle) -{ - Matrix result; - - Matrix mat = MatrixIdentity(); - - float x = axis.x, y = axis.y, z = axis.z; - - float length = sqrt(x*x + y*y + z*z); - - if ((length != 1) && (length != 0)) - { - length = 1 / length; - x *= length; - y *= length; - z *= length; - } - - float s = sin(angle); - float c = cos(angle); - float t = 1-c; - - // Cache some matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - - // Construct the elements of the rotation matrix - float b00 = x*x*t + c, b01 = y*x*t + z*s, b02 = z*x*t - y*s; - float b10 = x*y*t - z*s, b11 = y*y*t + c, b12 = z*y*t + x*s; - float b20 = x*z*t + y*s, b21 = y*z*t - x*s, b22 = z*z*t + c; - - // Perform rotation-specific matrix multiplication - result.m0 = a00*b00 + a10*b01 + a20*b02; - result.m1 = a01*b00 + a11*b01 + a21*b02; - result.m2 = a02*b00 + a12*b01 + a22*b02; - result.m3 = a03*b00 + a13*b01 + a23*b02; - result.m4 = a00*b10 + a10*b11 + a20*b12; - result.m5 = a01*b10 + a11*b11 + a21*b12; - result.m6 = a02*b10 + a12*b11 + a22*b12; - result.m7 = a03*b10 + a13*b11 + a23*b12; - result.m8 = a00*b20 + a10*b21 + a20*b22; - result.m9 = a01*b20 + a11*b21 + a21*b22; - result.m10 = a02*b20 + a12*b21 + a22*b22; - result.m11 = a03*b20 + a13*b21 + a23*b22; - result.m12 = mat.m12; - result.m13 = mat.m13; - result.m14 = mat.m14; - result.m15 = mat.m15; - - return result; -}; - -// Create rotation matrix from axis and angle (version 2) -// TODO: Test this function -// NOTE: NO prototype defined! -Matrix MatrixFromAxisAngle2(Vector3 axis, float angle) -{ - Matrix result; - - VectorNormalize(&axis); - float axisX = axis.x, axisY = axis.y, axisZ = axis.y; - - // Calculate angles - float cosres = (float)cos(angle); - float sinres = (float)sin(angle); - float t = 1.0f - cosres; - - // Do the conversion math once - float tXX = t * axisX * axisX; - float tXY = t * axisX * axisY; - float tXZ = t * axisX * axisZ; - float tYY = t * axisY * axisY; - float tYZ = t * axisY * axisZ; - float tZZ = t * axisZ * axisZ; - - float sinX = sinres * axisX; - float sinY = sinres * axisY; - float sinZ = sinres * axisZ; - - result.m0 = tXX + cosres; - result.m1 = tXY + sinZ; - result.m2 = tXZ - sinY; - result.m3 = 0; - result.m4 = tXY - sinZ; - result.m5 = tYY + cosres; - result.m6 = tYZ + sinX; - result.m7 = 0; - result.m8 = tXZ + sinY; - result.m9 = tYZ - sinX; - result.m10 = tZZ + cosres; - result.m11 = 0; - result.m12 = 0; - result.m13 = 0; - result.m14 = 0; - result.m15 = 1; - - return result; -} - -// Returns rotation matrix for a given quaternion -Matrix MatrixFromQuaternion(Quaternion q) -{ - Matrix result = MatrixIdentity(); - - Vector3 axis; - float angle; - - QuaternionToAxisAngle(q, &axis, &angle); - - result = MatrixFromAxisAngle2(axis, angle); - - return result; -} - // Returns x-rotation matrix (angle in radians) Matrix MatrixRotateX(float angle) { @@ -704,22 +592,6 @@ Matrix MatrixScale(float x, float y, float z) return result; } -// Returns transformation matrix for a given translation, rotation and scale -// NOTE: Transformation order is rotation -> scale -> translation -// NOTE: Rotation angles should come in radians -Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale) -{ - Matrix result = MatrixIdentity(); - - Matrix mRotation = MatrixRotate(rotation.x, rotation.y, rotation.z); - Matrix mScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix mTranslate = MatrixTranslate(translation.x, translation.y, translation.z); - - result = MatrixMultiply(MatrixMultiply(mRotation, mScale), mTranslate); - - return result; -} - // Returns two matrix multiplication // NOTE: When multiplying matrices... the order matters! Matrix MatrixMultiply(Matrix left, Matrix right) @@ -874,7 +746,7 @@ void PrintMatrix(Matrix m) // Module Functions Definition - Quaternion math //---------------------------------------------------------------------------------- -// Calculates the length of a quaternion +// Computes the length of a quaternion float QuaternionLength(Quaternion quat) { return sqrt(quat.x*quat.x + quat.y*quat.y + quat.z*quat.z + quat.w*quat.w); @@ -948,7 +820,7 @@ Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) return result; } -// Returns a quaternion from a given rotation matrix +// Returns a quaternion for a given rotation matrix Quaternion QuaternionFromMatrix(Matrix matrix) { Quaternion result; @@ -1004,29 +876,7 @@ Quaternion QuaternionFromMatrix(Matrix matrix) return result; } -// Returns rotation quaternion for an angle around an axis -// NOTE: angle must be provided in radians -Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0, 0, 0, 1 }; - - if (VectorLength(axis) != 0.0) - - angle *= 0.5; - - VectorNormalize(&axis); - - result.x = axis.x * (float)sin(angle); - result.y = axis.y * (float)sin(angle); - result.z = axis.z * (float)sin(angle); - result.w = (float)cos(angle); - - QuaternionNormalize(&result); - - return result; -} - -// Calculates the matrix from the given quaternion +// Returns a matrix for a given quaternion Matrix QuaternionToMatrix(Quaternion q) { Matrix result; @@ -1065,12 +915,34 @@ Matrix QuaternionToMatrix(Quaternion q) result.m13 = 0; result.m14 = 0; result.m15 = 1; + + return result; +} + +// Returns rotation quaternion for an angle and axis +// NOTE: angle must be provided in radians +Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis) +{ + Quaternion result = { 0, 0, 0, 1 }; + + if (VectorLength(axis) != 0.0) + + angle *= 0.5; + + VectorNormalize(&axis); + + result.x = axis.x * (float)sin(angle); + result.y = axis.y * (float)sin(angle); + result.z = axis.z * (float)sin(angle); + result.w = (float)cos(angle); + + QuaternionNormalize(&result); return result; } -// Returns the axis and the angle for a given quaternion -void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) +// Returns the rotation angle and axis for a given quaternion +void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis) { if (fabs(q.w) > 1.0f) QuaternionNormalize(&q); diff --git a/src/raymath.h b/src/raymath.h index 133e1881..d93c324c 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -107,15 +107,11 @@ Matrix MatrixIdentity(void); // Returns identity matr Matrix MatrixAdd(Matrix left, Matrix right); // Add two matrices Matrix MatrixSubstract(Matrix left, Matrix right); // Substract two matrices (left - right) Matrix MatrixTranslate(float x, float y, float z); // Returns translation matrix -Matrix MatrixRotate(float axisX, float axisY, float axisZ); // Returns rotation matrix -Matrix MatrixFromAxisAngle(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis -Matrix MatrixFromAxisAngle2(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis (test another implemntation) -Matrix MatrixFromQuaternion(Quaternion q); // Returns rotation matrix for a given quaternion +Matrix MatrixRotate(float angle, Vector3 axis); // Returns rotation matrix for an angle around an specified axis (angle in radians) Matrix MatrixRotateX(float angle); // Returns x-rotation matrix (angle in radians) Matrix MatrixRotateY(float angle); // Returns y-rotation matrix (angle in radians) Matrix MatrixRotateZ(float angle); // Returns z-rotation matrix (angle in radians) Matrix MatrixScale(float x, float y, float z); // Returns scaling matrix -Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale); // Returns transformation matrix for a given translation, rotation and scale Matrix MatrixMultiply(Matrix left, Matrix right); // Returns two matrix multiplication Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far); // Returns perspective projection matrix Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix @@ -126,14 +122,14 @@ void PrintMatrix(Matrix m); // Print matrix utility //------------------------------------------------------------------------------------ // Functions Declaration to work with Quaternions //------------------------------------------------------------------------------------ -float QuaternionLength(Quaternion quat); // Calculates the length of a quaternion +float QuaternionLength(Quaternion quat); // Compute the length of a quaternion void QuaternionNormalize(Quaternion *q); // Normalize provided quaternion Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float slerp); // Calculates spherical linear interpolation between two quaternions -Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion from a given rotation matrix -Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle); // Returns rotation quaternion for an angle around an axis -Matrix QuaternionToMatrix(Quaternion q); // Calculates the matrix from the given quaternion -void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle); // Returns the axis and the angle for a given quaternion +Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix +Matrix QuaternionToMatrix(Quaternion q); // Returns a matrix for a given quaternion +Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis); // Returns rotation quaternion for an angle and axis +void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis); // Returns the rotation angle and axis for a given quaternion void QuaternionTransform(Quaternion *q, Matrix mat); // Transform a quaternion given a transformation matrix #ifdef __cplusplus diff --git a/src/rlgl.c b/src/rlgl.c index fbea84a2..7a73623c 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -30,6 +30,7 @@ #include // Standard input / output lib #include // Declares malloc() and free() for memory management, rand() +#include // Declares strcmp(), strlen(), strtok(), strdup() #if defined(GRAPHICS_API_OPENGL_11) #ifdef __APPLE__ // OpenGL include for OSX @@ -63,28 +64,49 @@ #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) -#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 -#endif -#ifndef GL_ETC1_RGB8_OES - #define GL_ETC1_RGB8_OES 0x8D64 -#endif -#ifndef GL_COMPRESSED_RGB8_ETC2 - #define GL_COMPRESSED_RGB8_ETC2 0x9274 -#endif -#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC - #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#ifndef GL_SHADING_LANGUAGE_VERSION + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #endif +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR + #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -215,6 +237,8 @@ static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: It's required in shapes and models modules! unsigned int whiteTexture; +static bool supportedTextureFormat[32]; + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -224,9 +248,6 @@ static Shader LoadSimpleShader(void); static void InitializeBuffers(void); static void InitializeBuffersGPU(void); static void UpdateBuffers(void); -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); - -// Custom shader files loading (external) static char *TextFileRead(char *fn); #endif @@ -235,6 +256,8 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); #endif +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); + //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations //---------------------------------------------------------------------------------- @@ -328,23 +351,11 @@ void rlTranslatef(float x, float y, float z) // Multiply the current matrix by a rotation matrix void rlRotatef(float angleDeg, float x, float y, float z) { - // TODO: Support rotation in multiple axes Matrix rotation = MatrixIdentity(); - // OPTION 1: It works... - 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); - - // OPTION 3: TODO: Review, it doesn't work! - //Vector3 vec = (Vector3){ x, y, z }; - //VectorNormalize(&vec); - //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x); + Vector3 axis = (Vector3){ x, y, z }; + VectorNormalize(&axis); + rotation = MatrixRotate(angleDeg*DEG2RAD, axis); MatrixTranspose(&rotation); @@ -840,7 +851,7 @@ void rlglInit(void) TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); TraceLog(INFO, "GPU: Version: %s", glGetString(GL_VERSION)); - TraceLog(INFO, "GPU: GLSL: %s", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION + TraceLog(INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) //int maxTexSize; @@ -853,6 +864,9 @@ void rlglInit(void) // Show supported extensions // NOTE: We don't need that much data on screen... right now... + + // Check available extensions for compressed textures support + for (int i = 0; i < 32; i++) supportedTextureFormat[i] = false; #if defined(GRAPHICS_API_OPENGL_33) GLint numExt; @@ -861,40 +875,49 @@ void rlglInit(void) for (int i = 0; i < numExt; i++) { //TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); - /* - if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_EXT_texture_compression_s3tc") == 0) + + if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_EXT_texture_compression_s3tc") == 0) { // DDS texture compression support - - // TODO: Check required tokens + supportedTextureFormat[COMPRESSED_DXT1_RGB] = true; + supportedTextureFormat[COMPRESSED_DXT1_RGBA] = true; + supportedTextureFormat[COMPRESSED_DXT3_RGBA] = true; + supportedTextureFormat[COMPRESSED_DXT5_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_OES_compressed_ETC1_RGB8_texture") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_OES_compressed_ETC1_RGB8_texture") == 0) { // ETC1 texture compression support + supportedTextureFormat[COMPRESSED_ETC1_RGB] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) { - //OES_compressed_ETC2_RGB8_texture, - //OES_compressed_ETC2_RGBA8_texture, // ETC2/EAC texture compression support + supportedTextureFormat[COMPRESSED_ETC2_RGB] = true; + supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) { // PVR texture compression support + supportedTextureFormat[COMPRESSED_PVRT_RGB] = true; + supportedTextureFormat[COMPRESSED_PVRT_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) { // ASTC texture compression support + supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA] = true; + supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA] = true; } - */ } #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string // NOTE: String could be splitted using strtok() function (string.h) TraceLog(INFO, "Supported extension: %s", extensions); + + //char** ext = StringSplit(extensions, ' '); + //for (int i = 0; i < numExt; i++) printf("%s", ext[i]); + #endif - /* GLint numComp = 0; glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -1258,6 +1281,7 @@ void rlglDrawPostpro(void) } // Draw a 3d model +// NOTE: Model transform can come within model struct void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires) { #if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) @@ -1284,8 +1308,6 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r rlScalef(scale.x, scale.y, scale.z); rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); - // TODO: If rotate in multiple axis, get rotation matrix and use rlMultMatrix() - rlColor4ub(color.r, color.g, color.b, color.a); glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); @@ -1302,13 +1324,17 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(model.shader.id); - // TODO: Use model.transform matrix - - Vector3 rotation = { 0.0f, 0.0f, 0.0f }; + // Apply transformation provided in model.transform matrix + Matrix modelviewworld = MatrixMultiply(model.transform, modelview); // World-space transformation + // Apply transformations provided in function // Get transform matrix (rotation -> scale -> translation) - Matrix transform = MatrixTransform(position, rotation, scale); // Object-space transformation - Matrix modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation + Matrix rotation = MatrixRotate(rotationAngle*DEG2RAD, rotationAxis); + Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); + Matrix translation = MatrixTranslate(position.x, position.y, position.z); + + Matrix transform = MatrixMultiply(MatrixMultiply(rotation, matScale), translation); // Object-space transformation matrix + modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation // Projection: Screen-space transformation @@ -1405,7 +1431,6 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif - // TODO: Review this comment when called from window resize callback TraceLog(INFO, "OpenGL Graphics initialized successfully"); } @@ -1596,75 +1621,6 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma //glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, id); - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used! -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis -#endif - - bool texIsPOT = false; - - // Check if width and height are power-of-two (POT) - if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; - - if (genMipmaps && !texIsPOT) - { - TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); - - genMipmaps = false; - } - - // TODO: Support mipmaps --> if (mipmapCount > 1) - - // If mipmaps are being used, we configure mag-min filters accordingly - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so only GL_LINEAR or GL_NEAREST can be used - if (genMipmaps) - { - // Trilinear filtering with mipmaps - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available) - } - else - { - // Not using mipmappings - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - } - -#if defined(GRAPHICS_API_OPENGL_11) - if (genMipmaps) - { - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); - - // Compute required mipmaps - // NOTE: data size is reallocated to fit mipmaps data - int mipmapCount = GenerateMipmaps(data, width, height); - - int offset = 0; - int size = 0; - - int mipWidth = width; - int mipHeight = height; - - // Load the mipmaps - for (int level = 0; level < mipmapCount; level++) - { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset); - - size = mipWidth*mipHeight*4; - offset += size; - - mipWidth /= 2; - mipHeight /= 2; - } - } - else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); -#endif - #if defined(GRAPHICS_API_OPENGL_33) // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) // NOTE: On embedded systems, we let the driver choose the best internal format @@ -1703,24 +1659,20 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; - case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; - case COMPRESSED_ETC1_RGB: TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break;//TraceLog(WARNING, "ETC compression not supported"); 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;//TraceLog(WARNING, "ETC compression not supported"); 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 + case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; + case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; + case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[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: if (supportedTextureFormat[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_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 default: TraceLog(WARNING, "Texture format not recognized"); break; } - - if ((mipmapCount == 1) && (genMipmaps)) - { - glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); - } -#elif defined(GRAPHICS_API_OPENGL_ES2) - +#elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA switch (textureFormat) { @@ -1731,17 +1683,63 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma 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; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_ETC1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; - 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 + case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[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: if (supportedTextureFormat[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_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 default: TraceLog(WARNING, "Texture format not supported"); break; } +#endif + // Check if texture is power-of-two (POT) to enable mipmap generation + bool texIsPOT = false; + + if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; + + if (genMipmaps && !texIsPOT) + { + TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); + genMipmaps = false; + } + + // Generate mipmaps if required + // TODO: Improve mipmaps support +#if defined(GRAPHICS_API_OPENGL_11) + if (genMipmaps) + { + // Compute required mipmaps + // NOTE: data size is reallocated to fit mipmaps data + int mipmapCount = GenerateMipmaps(data, width, height); + + // TODO: Adjust mipmap size depending on texture format! + int size = width*height*4; + int offset = size; + + int mipWidth = width/2; + int mipHeight = height/2; + + // Load the mipmaps + for (int level = 1; level < mipmapCount; level++) + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset); + + size = mipWidth*mipHeight*4; + offset += size; + + mipWidth /= 2; + mipHeight /= 2; + } + + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); + } +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((mipmapCount == 1) && (genMipmaps)) { glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically @@ -1749,7 +1747,30 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma } #endif - // At this point we have the image converted to texture and uploaded to GPU + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if ((mipmapCount > 1) || (genMipmaps)) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) + } +#endif + + // At this point we have the texture loaded in GPU, with mipmaps generated (if desired) and texture parameters configured // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); @@ -2027,6 +2048,7 @@ void rlglSetModelShader(Model *model, Shader shader) // Set custom shader to be used on batch draw void rlglSetCustomShader(Shader shader) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (currentShader.id != shader.id) { rlglDraw(); @@ -2053,12 +2075,15 @@ void rlglSetCustomShader(Shader shader) if (vaoSupported) glBindVertexArray(0); // Unbind VAO */ } +#endif } // Set default shader to be used on batch draw void rlglSetDefaultShader(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlglSetCustomShader(defaultShader); +#endif } #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -2441,7 +2466,7 @@ static void InitializeBuffersGPU(void) // Update VBOs with vertex array data // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) -// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays every frame! +// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays static void UpdateBuffers(void) { if (lines.vCounter > 0) @@ -2508,11 +2533,9 @@ static void UpdateBuffers(void) // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); } - #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_11) - // Mipmaps data is generated after image data static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) { diff --git a/src/text.c b/src/text.c index 8e3fc4b9..b4fb54af 100644 --- a/src/text.c +++ b/src/text.c @@ -84,12 +84,6 @@ extern void LoadDefaultFont(void) defaultFont.numChars = 224; // Number of chars included in our default font - 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 // This data is automatically allocated to Stack and automatically deallocated at the end of this function @@ -151,14 +145,17 @@ extern void LoadDefaultFont(void) // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- - Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + int imWidth = 128; + int imHeight = 128; + + Color *imagePixels = (Color *)malloc(imWidth*imHeight*sizeof(Color)); - for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array + for (int i = 0; i < imWidth*imHeight; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter // Fill imgData with defaultFontData (convert from bit to pixel!) - for (int i = 0; i < image.width * image.height; i += 32) + for (int i = 0; i < imWidth*imHeight; i += 32) { for (int j = 31; j >= 0; j--) { @@ -174,7 +171,7 @@ extern void LoadDefaultFont(void) //fwrite(image.pixels, 1, 128*128*4, myimage); //fclose(myimage); - SetPixelData(&image, imagePixels, 0); + Image image = LoadImageFromData(imagePixels, imWidth, imHeight, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); @@ -507,7 +504,6 @@ static SpriteFont LoadRBMF(const char *fileName) } rbmfInfoHeader; SpriteFont spriteFont; - Image image; rbmfInfoHeader rbmfHeader; unsigned int *rbmfFileData = NULL; @@ -529,11 +525,6 @@ static SpriteFont LoadRBMF(const char *fileName) spriteFont.numChars = (int)rbmfHeader.numChars; - image.width = (int)rbmfHeader.imgWidth; - image.height = (int)rbmfHeader.imgHeight; - image.mipmaps = 1; - image.format = UNCOMPRESSED_R8G8B8A8; - int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); @@ -546,14 +537,14 @@ static SpriteFont LoadRBMF(const char *fileName) // Re-construct image from rbmfFileData //----------------------------------------- - Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + Color *imagePixels = (Color *)malloc(rbmfHeader.imgWidth*rbmfHeader.imgHeight*sizeof(Color)); - for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array + for (int i = 0; i < rbmfHeader.imgWidth*rbmfHeader.imgHeight; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter // Fill image data (convert from bit to pixel!) - for (int i = 0; i < image.width * image.height; i += 32) + for (int i = 0; i < rbmfHeader.imgWidth*rbmfHeader.imgHeight; i += 32) { for (int j = 31; j >= 0; j--) { @@ -563,7 +554,7 @@ static SpriteFont LoadRBMF(const char *fileName) counter++; } - SetPixelData(&image, imagePixels, 0); + Image image = LoadImageFromData(imagePixels, rbmfHeader.imgWidth, rbmfHeader.imgHeight, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); @@ -694,7 +685,6 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) print(100,160, 0, "This is a test"); */ - font.numChars = 95; font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); font.texture = LoadTextureFromImage(image, false); diff --git a/src/textures.c b/src/textures.c index d2b2de1d..176ed2d7 100644 --- a/src/textures.c +++ b/src/textures.c @@ -280,7 +280,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) 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; @@ -439,24 +439,141 @@ Color *GetPixelData(Image image) } // 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) +// NOTE: Data is transformed to desired format +Image LoadImageFromData(Color *pixels, int width, int height, int format) { - free(image->data); - image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char)); - + Image image; + image.data = NULL; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = format; + int k = 0; - for (int i = 0; i < image->width*image->height*4; i += 4) + switch (format) { - ((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++; + case UNCOMPRESSED_GRAYSCALE: + { + image.data = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height; i++) + { + ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + k++; + } + + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + image.data = (unsigned char *)malloc(image.width*image.height*2*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*2; i += 2) + { + ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + ((unsigned char *)image.data)[i + 1] = pixels[k].a; + k++; + } + + } break; + case UNCOMPRESSED_R5G6B5: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*63/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + + ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; + + k++; + } + + } break; + case UNCOMPRESSED_R8G8B8: + { + image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*3; i += 3) + { + ((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; + k++; + } + } break; + case UNCOMPRESSED_R5G5B5A1: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a = 1; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*31/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + a = (pixels[k].a > 50) ? 1 : 0; + + ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1| (unsigned short)a; + + k++; + } + + } break; + case UNCOMPRESSED_R4G4B4A4: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*15/255)); + g = (unsigned char)(round((float)pixels[k].g*15/255)); + b = (unsigned char)(round((float)pixels[k].b*15/255)); + a = (unsigned char)(round((float)pixels[k].a*15/255)); + + ((unsigned short *)image.data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a; + + k++; + } + + } break; + case UNCOMPRESSED_R8G8B8A8: + { + image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); + + 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++; + } + } break; + default: + { + TraceLog(WARNING, "Format not recognized, image could not be loaded"); + + return image; + } break; } + + return image; } // Draw a Texture2D