From 7d2318c1677fd6e81b8cc63b040289148ca8adfc Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 13 Apr 2015 20:15:28 +0200 Subject: [PATCH] WIP on shaders and textures Moved functions: LoadShader(), UnloadShader() Add support for PVR textures compressed/uncompressed WIP: Detect available extensions for compressed textures --- src/models.c | 21 +++++++++ src/raylib.h | 3 ++ src/rlgl.c | 52 +++++++++++++++------ src/rlgl.h | 6 ++- src/textures.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+), 14 deletions(-) diff --git a/src/models.c b/src/models.c index f4f83fd8..ffb077d4 100644 --- a/src/models.c +++ b/src/models.c @@ -1101,12 +1101,33 @@ void UnloadModel(Model model) rlDeleteShader(model.shader.id); } +// Link a texture to a model void SetModelTexture(Model *model, Texture2D texture) { if (texture.id <= 0) model->texture.id = whiteTexture; // Default white texture (use mesh color) else model->texture = texture; } +// Load a custom shader (vertex shader + fragment shader) +Shader LoadShader(char *vsFileName, char *fsFileName) +{ + Shader shader = rlglLoadShader(vsFileName, fsFileName); + + return shader; +} + +// Unload a custom shader from memory +void UnloadShader(Shader shader) +{ + rlDeleteShader(shader.id); +} + +// Set shader for a model +void SetModelShader(Model *model, Shader shader) +{ + rlglSetModelShader(model, shader); +} + // Draw a model (with texture if set) void DrawModel(Model model, Vector3 position, float scale, Color tint) { diff --git a/src/raylib.h b/src/raylib.h index 0e532ebe..603cc47d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -338,6 +338,8 @@ typedef enum { COMPRESSED_ETC1_RGB, // 4 bpp COMPRESSED_ETC2_RGB, // 4 bpp COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp } TextureFormat; @@ -543,6 +545,7 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) +void UnloadShader(Shader shader); // Unload a custom shader from memory bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); diff --git a/src/rlgl.c b/src/rlgl.c index c4deda8f..cedad6a7 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -793,7 +793,7 @@ void rlglInit(void) const GLubyte *extensionName = glGetStringi(GL_EXTENSIONS, i); if (strcmp(extensionName, (const GLubyte *)"GL_ARB_vertex_array_object") == 0) { - // The extension is supported by our hardware and driver, try to get related functions popinters + // The extension is supported by our hardware and driver, try to get related functions pointers glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)wglGetProcAddress("glGenVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)wglGetProcAddress("glBindVertexArray"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)wglGetProcAddress("glDeleteVertexArrays"); @@ -851,14 +851,40 @@ void rlglInit(void) // Show supported extensions // NOTE: We don't need that much data on screen... right now... -/* + #if defined(GRAPHICS_API_OPENGL_33) GLint numExt; glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); for (int i = 0; i < numExt; i++) { - TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); + //TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); + /* + if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_EXT_texture_compression_s3tc") == 0) + { + // DDS texture compression support + + // TODO: Check required tokens + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_OES_compressed_ETC1_RGB8_texture") == 0) + { + // ETC1 texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) + { + //OES_compressed_ETC2_RGB8_texture, + //OES_compressed_ETC2_RGBA8_texture, + // ETC2/EAC texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) + { + // PVR texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) + { + // ASTC texture compression support + } + */ } #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string @@ -866,7 +892,7 @@ void rlglInit(void) // NOTE: String could be splitted using strtok() function (string.h) TraceLog(INFO, "Supported extension: %s", extensions); #endif -*/ + /* GLint numComp = 0; glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -894,7 +920,7 @@ void rlglInit(void) // Init default Shader (GLSL 110) -> Common for GL 3.3+ and ES2 defaultShader = LoadDefaultShader(); simpleShader = LoadSimpleShader(); - //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok + //customShader = rlglLoadShader("custom.vs", "custom.fs"); // Works ok InitializeBuffers(); // Init vertex arrays InitializeBuffersGPU(); // Init VBO and VAO @@ -1795,7 +1821,7 @@ Model rlglLoadModel(VertexData mesh) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load a shader (vertex shader + fragment shader) from text data -unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) +unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr) { unsigned int program; GLuint vertexShader; @@ -1915,17 +1941,17 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -// Load a shader (vertex shader + fragment shader) from text data -Shader LoadShader(char *vsFileName, char *fsFileName) +// Load a shader (vertex shader + fragment shader) from files +Shader rlglLoadShader(char *vsFileName, char *fsFileName) { Shader shader; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Shaders loading from external text file char *vShaderStr = TextFileRead(vsFileName); char *fShaderStr = TextFileRead(fsFileName); - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shader.id); @@ -1955,7 +1981,7 @@ Shader LoadShader(char *vsFileName, char *fsFileName) } // Link shader to model -void SetModelShader(Model *model, Shader shader) +void rlglSetModelShader(Model *model, Shader shader) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model->shader = shader; @@ -2079,7 +2105,7 @@ static Shader LoadDefaultShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" "} \n"; - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); @@ -2145,7 +2171,7 @@ static Shader LoadSimpleShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * tintColor; \n" "} \n"; - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id); diff --git a/src/rlgl.h b/src/rlgl.h index c1d71cc9..3335fe16 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -104,6 +104,8 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; COMPRESSED_ETC1_RGB, // 4 bpp COMPRESSED_ETC2_RGB, // 4 bpp COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp } TextureFormat; @@ -208,10 +210,12 @@ void rlglDraw(void); // Draw VAO/VBO void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load in GPU OpenGL texture -unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data +Shader rlglLoadShader(char *vsFileName, char *fsFileName); // Load a shader (vertex shader + fragment shader) from files +unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr); // Load a shader from text data void rlglInitPostpro(void); // Initialize postprocessing system 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 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/textures.c b/src/textures.c index e45739ac..1d22e509 100644 --- a/src/textures.c +++ b/src/textures.c @@ -70,6 +70,7 @@ typedef struct { //---------------------------------------------------------------------------------- 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 //---------------------------------------------------------------------------------- // Module Functions Definition @@ -326,6 +327,20 @@ Texture2D LoadTexture(const char *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); @@ -694,6 +709,7 @@ 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) { // If OpenGL ES 2.0. the following format could be supported (ETC1): @@ -771,5 +787,110 @@ static ImageEx LoadPKM(const char *fileName) } } + 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) +{ + // 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) + + // PVR file v2 Header (52 bytes) + typedef struct { + unsigned int headerLength; + unsigned int height; + unsigned int width; + unsigned int numMipmaps; + unsigned int flags; + unsigned int dataLength; + unsigned int bpp; + unsigned int bitmaskRed; + unsigned int bitmaskGreen; + unsigned int bitmaskBlue; + unsigned int bitmaskAlpha; + unsigned int pvrTag; + unsigned int numSurfs; + } pvrHeaderV2; + + // 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; + } pvrHeaderV3; + + // Bytes Swap (little-endian <-> big-endian) + //unsigned short data; + //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8); + + ImageEx image; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; + + FILE *pvrFile = fopen(fileName, "rb"); + + if (pvrFile == NULL) + { + TraceLog(WARNING, "[%s] PVR image 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); + + 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) + { + pvrHeaderV3 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 + } + + int size = (image.width/4)*(image.height/4)*8; // Total data size in bytes + + image.data = (unsigned char*)malloc(size * sizeof(unsigned char)); + + fread(image.data, 1, size, pvrFile); + + fclose(pvrFile); // Close file pointer + } + return image; } \ No newline at end of file