From 96f520ff6d085536205feb1f943974e354577df4 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 25 Mar 2014 12:40:35 +0100 Subject: Road to raylib 1.1 - Testing rlgl --- src/text.c | 154 +++++++++++++++++++++++++++---------------------------------- 1 file changed, 69 insertions(+), 85 deletions(-) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index 492329c5..b897f1f3 100644 --- a/src/text.c +++ b/src/text.c @@ -28,12 +28,13 @@ #include "raylib.h" -#include // OpenGL functions #include // Declares malloc() and free() for memory management #include // String management functions (just strlen() is used) #include // Used for functions with variable number of parameters (FormatText()) #include "stb_image.h" // Used to read image data (multiple formats support) +#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -63,6 +64,11 @@ struct Character { static SpriteFont defaultFont; // Default font provided by raylib // NOTE: defaultFont is loaded on InitWindow and disposed on CloseWindow [module: core] +//---------------------------------------------------------------------------------- +// Other Modules Functions Declaration (required by text) +//---------------------------------------------------------------------------------- +//... + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -78,9 +84,11 @@ static const char *GetExtension(const char *fileName); extern void LoadDefaultFont() { defaultFont.numChars = 96; // We know our default font has 94 chars - defaultFont.texture.width = 128; // We know our default font texture is 128 pixels width - defaultFont.texture.height = 64; // We know our default font texture is 64 pixels height - + + Image image; + image.width = 128; // We know our default font image is 128 pixels width + image.height = 64; // We know our default font image is 64 pixels height + // 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 @@ -115,7 +123,31 @@ extern void LoadDefaultFont() 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4 }; + // Re-construct image from defaultFontData and generate OpenGL texture + //---------------------------------------------------------------------- + image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); + + for (int i = 0; i < image.width * image.height; i++) image.pixels[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 j = 31; j >= 0; j--) + { + if (BIT_CHECK(defaultFontData[counter], j)) image.pixels[i+j] = WHITE; + } + + counter++; + + if (counter > 256) counter = 0; // Security check... + } + + defaultFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture + UnloadImage(image); + // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars //------------------------------------------------------------------------------ defaultFont.charSet = (Character *)malloc(defaultFont.numChars * sizeof(Character)); // Allocate space for our character data @@ -145,53 +177,11 @@ extern void LoadDefaultFont() } else currentPosX = testPosX; } - - // Re-construct image from defaultFontData and generate OpenGL texture - //---------------------------------------------------------------------- - Color *imgDataPixel = (Color *)malloc(defaultFont.texture.width * defaultFont.texture.height * sizeof(Color)); - - for (int i = 0; i < defaultFont.texture.width * defaultFont.texture.height; i++) imgDataPixel[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 < defaultFont.texture.width * defaultFont.texture.height; i += 32) - { - for (int j = 31; j >= 0; j--) - { - if (BIT_CHECK(defaultFontData[counter], j)) imgDataPixel[i+j] = WHITE; - } - - counter++; - - if (counter > 256) counter = 0; // Security check... - } - - // Convert loaded data to OpenGL texture - //---------------------------------------- - GLuint id; - glGenTextures(1, &id); // Generate pointer to the texture - - glBindTexture(GL_TEXTURE_2D, id); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // Set texture to clamp on y-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, defaultFont.texture.width, defaultFont.texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgDataPixel); - - // NOTE: Not using mipmappings (texture for 2D drawing) - // At this point we have the image converted to texture and uploaded to GPU - - free(imgDataPixel); // Now we can free loaded data from RAM memory - - defaultFont.texture.glId = id; } extern void UnloadDefaultFont() { - glDeleteTextures(1, &defaultFont.texture.glId); + rlDeleteTextures(defaultFont.texture.glId); free(defaultFont.charSet); } @@ -206,6 +196,8 @@ SpriteFont LoadSpriteFont(const char* fileName) { SpriteFont spriteFont; + Image image; + // Check file extension if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); else @@ -270,24 +262,13 @@ SpriteFont LoadSpriteFont(const char* fileName) free(imgDataPixel); - // Convert loaded data to OpenGL texture - //---------------------------------------- - GLuint id; - glGenTextures(1, &id); // Generate pointer to the texture - - glBindTexture(GL_TEXTURE_2D, id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgDataPixelPOT); - - // NOTE: Not using mipmappings (texture for 2D drawing) - // At this point we have the image converted to texture and uploaded to GPU - - free(imgDataPixelPOT); // Now we can free loaded data from RAM memory + image.pixels = imgDataPixelPOT; + image.width = potWidth; + image.height = potHeight; - spriteFont.texture.glId = id; - spriteFont.texture.width = potWidth; - spriteFont.texture.height = potHeight; + spriteFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture + + UnloadImage(image); } return spriteFont; @@ -296,7 +277,7 @@ SpriteFont LoadSpriteFont(const char* fileName) // Unload SpriteFont from GPU memory void UnloadSpriteFont(SpriteFont spriteFont) { - glDeleteTextures(1, &spriteFont.texture.glId); + rlDeleteTextures(spriteFont.texture.glId); free(spriteFont.charSet); } @@ -330,28 +311,34 @@ void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int f if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, spriteFont.texture.glId); + rlEnableTexture(spriteFont.texture.glId); // Optimized to use one draw call per string - glBegin(GL_QUADS); + rlBegin(RL_QUADS); for(int i = 0; i < length; i++) { c = spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR]; - glColor4ub(tint.r, tint.g, tint.b, tint.a); - glNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer - glTexCoord2f((float)c.x / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); glVertex2f(positionX, position.y); - glTexCoord2f((float)c.x / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); glVertex2f(positionX, position.y + (c.h) * scaleFactor); - glTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); glVertex2f(positionX + (c.w) * scaleFactor, position.y + (c.h) * scaleFactor); - glTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); glVertex2f(positionX + (c.w) * scaleFactor, position.y); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + + rlTexCoord2f((float)c.x / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); + rlVertex2f(positionX, position.y); + + rlTexCoord2f((float)c.x / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); + rlVertex2f(positionX, position.y + (c.h) * scaleFactor); + + rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); + rlVertex2f(positionX + (c.w) * scaleFactor, position.y + (c.h) * scaleFactor); + + rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); + rlVertex2f(positionX + (c.w) * scaleFactor, position.y); positionX += ((spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR].w) * scaleFactor + spacing); } - glEnd(); - - glDisable(GL_TEXTURE_2D); + rlEnd(); + + rlDisableTexture(); } // Formatting of text with variables to 'embed' @@ -417,21 +404,18 @@ void DrawFPS(int posX, int posY) char buffer[20]; if (counter < refreshRate) - { - sprintf(buffer, "%2.0f FPS", fps); - DrawText(buffer, posX, posY, 20, LIME); - + { counter++; } else { fps = GetFPS(); refreshRate = fps; - sprintf(buffer, "%2.0f FPS", fps); - DrawText(buffer, posX, posY, 20, LIME); - counter = 0; } + + sprintf(buffer, "%2.0f FPS", fps); + DrawText(buffer, posX, posY, 20, LIME); } //---------------------------------------------------------------------------------- -- cgit v1.2.3 From c04f37d0f5f75c3e112d71444e589513396c9d0f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 4 Apr 2014 20:11:57 +0200 Subject: Adapt multiple functions to rlgl Nearly a complete rework of Models module Some teaks on multiple functions --- src/audio.c | 4 +- src/models.c | 866 ++++++++++++++++++++++++++++++++++----------------------- src/raylib.h | 6 +- src/rlgl.c | 150 ++++++++-- src/rlgl.h | 46 ++- src/shapes.c | 29 +- src/text.c | 1 - src/textures.c | 102 +++---- 8 files changed, 755 insertions(+), 449 deletions(-) (limited to 'src/text.c') diff --git a/src/audio.c b/src/audio.c index 4b75942f..ac6fc2b3 100644 --- a/src/audio.c +++ b/src/audio.c @@ -131,7 +131,7 @@ Sound LoadSound(char *fileName) // NOTE: Buffer space is allocated inside LoadWAV, Wave must be freed Wave wave = LoadWAV(fileName); - ALenum format; + ALenum format = 0; // The OpenAL format is worked out by looking at the number of channels and the bits per sample if (wave.channels == 1) { @@ -257,7 +257,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId) free(data); // Convert wave to Sound (OpenAL) - ALenum format; + ALenum format = 0; // The OpenAL format is worked out by looking at the number of channels and the bits per sample if (wave.channels == 1) diff --git a/src/models.c b/src/models.c index e1b80617..fb8f114d 100644 --- a/src/models.c +++ b/src/models.c @@ -31,7 +31,6 @@ #include // Used for sin, cos, tan #include "raymath.h" // Required for data type Matrix and Matrix functions - #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 //---------------------------------------------------------------------------------- @@ -42,30 +41,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -#ifdef USE_OPENGL_11 - struct Model { - int numVertices; - Vector3 *vertices; - Vector2 *texcoords; - Vector3 *normals; - }; -#else - struct Model { - int numVertices; - Vector3 *vertices; - Vector2 *texcoords; - Vector3 *normals; - }; - -/* - struct Model - { - GLUint vaoId; - Matrix transform; - int polyMode; - } -*/ -#endif +// ... //---------------------------------------------------------------------------------- // Global Variables Definition @@ -85,25 +61,167 @@ static float GetHeightValue(Color pixel); // NOTE: Cube position is the center position void DrawCube(Vector3 position, float width, float height, float lenght, Color color) { - // THIS WORKS! -/* - Matrix mat = MatrixTranslate(2.0, 0.0, 0.0); - MatrixTranspose(&mat); - VectorTransform(&position, mat); - - PrintMatrix(mat); -*/ - float x = position.x; float y = position.y; float z = position.z; rlPushMatrix(); + + // NOTE: Be careful! Function order matters (scale, translate, rotate) + //rlScalef(2.0f, 2.0f, 2.0f); + //rlTranslatef(0.0f, 0.0f, 0.0f); + //rlRotatef(45, 0, 1, 0); + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // Front Face ----------------------------------------------------- + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + + // Back Face ------------------------------------------------------ + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + + // Top Face ------------------------------------------------------- + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Bottom Left + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Bottom Right + + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Bottom Right + + // Bottom Face ---------------------------------------------------- + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left + + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Top Right + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left + + // Right face ----------------------------------------------------- + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Left + + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Left + + // Left Face ------------------------------------------------------ + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Right + + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Right + rlEnd(); + rlPopMatrix(); +} + +// Draw cube (Vector version) +void DrawCubeV(Vector3 position, Vector3 size, Color color) +{ + DrawCube(position, size.x, size.y, size.z, color); +} + +// Draw cube wires +void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color) +{ + float x = position.x; + float y = position.y; + float z = position.z; + + rlPushMatrix(); + + //rlRotatef(45, 0, 1, 0); + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // Front Face ----------------------------------------------------- + // Bottom Line + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + + // Left Line + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right + + // Top Line + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + + // Right Line + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Bottom Left + // Back Face ------------------------------------------------------ + // Bottom Line + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + + // Left Line + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right + + // Top Line + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + + // Right Line + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Bottom Left + + // Top Face ------------------------------------------------------- + // Left Line + rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Left Front + rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Back + + // Right Line + rlVertex3f(x+width/2, y+height/2, z+lenght/2); // Top Right Front + rlVertex3f(x+width/2, y+height/2, z-lenght/2); // Top Right Back + + // Bottom Face --------------------------------------------------- + // Left Line + rlVertex3f(x-width/2, y-height/2, z+lenght/2); // Top Left Front + rlVertex3f(x-width/2, y-height/2, z-lenght/2); // Top Left Back + + // Right Line + rlVertex3f(x+width/2, y-height/2, z+lenght/2); // Top Right Front + rlVertex3f(x+width/2, y-height/2, z-lenght/2); // Top Right Back + rlEnd(); + rlPopMatrix(); +} + +// Draw cube +// NOTE: Cube position is the center position +void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float lenght, Color color) +{ + float x = position.x; + float y = position.y; + float z = position.z; + + rlEnableTexture(texture.glId); + + rlPushMatrix(); // NOTE: Be careful! Function order matters (scale, translate, rotate) //rlScalef(2.0f, 2.0f, 2.0f); //rlTranslatef(2.0f, 0.0f, 0.0f); - rlRotatef(45, 0, 1, 0); + //rlRotatef(45, 0, 1, 0); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); @@ -145,22 +263,8 @@ void DrawCube(Vector3 position, float width, float height, float lenght, Color c rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Of The Texture and Quad rlEnd(); rlPopMatrix(); -} - -// Draw cube (Vector version) -void DrawCubeV(Vector3 position, Vector3 size, Color color) -{ - DrawCube(position, size.x, size.y, size.z, color); -} - -// Draw cube wires -void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color) -{ - // TODO: Draw cube using RL_LINES! - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - //DrawCube(position, width, height, lenght, color); - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + rlDisableTexture(); } // Draw sphere @@ -172,42 +276,37 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { - float lat0, z0, zr0; - float lat1, z1, zr1; - float lng, x, y; - - // TODO: Review vertex translate/rotate/scale mechanism - rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); - rlRotatef(90, 1, 0, 0); + //rlRotatef(rotation, 0, 1, 0); rlScalef(radius, radius, radius); - rlBegin(GL_QUAD_STRIP); - + rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - for(int i = 0; i <= rings; i++) + for(int i = 0; i < 2 * rings + 1; i ++) { - lat0 = PI * (-0.5 + (float)(i - 1) / rings); - z0 = sin(lat0); - zr0 = cos(lat0); - - lat1 = PI * (-0.5 + (float)i / rings); - z1 = sin(lat1); - zr1 = cos(lat1); - - for(int j = 0; j <= slices; j++) + for(int j = 0; j < slices; j++) { - lng = 2 * PI * (float)(j - 1) / slices; - x = cos(lng); - y = sin(lng); - - rlNormal3f(x * zr0, y * zr0, z0); - rlVertex3f(x * zr0, y * zr0, z0); - - rlNormal3f(x * zr1, y * zr1, z1); - rlVertex3f(x * zr1, y * zr1, z1); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*i)) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*i)) * radius, + cos(DEG2RAD*(270+(90/rings)*i)) * cos(DEG2RAD*(j*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*(j*360/slices)) * radius); + + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*i)) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*i)) * radius, + cos(DEG2RAD*(270+(90/rings)*i)) * cos(DEG2RAD*(j*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i))) * sin(DEG2RAD*((j+1)*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i))) * cos(DEG2RAD*((j+1)*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)) * radius); } } rlEnd(); @@ -215,134 +314,136 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color } // Draw sphere wires -void DrawSphereWires(Vector3 centerPos, float radius, Color color) -{ - // TODO: Draw sphere using RL_LINES! - - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - //DrawSphere(centerPos, radius, color); - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); -} - -// Draw a cylinder/cone -void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color) // Could be used for pyramid and cone! +void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { - Vector3 a = { position.x, position.y + height, position.z }; - Vector3 d = { 0.0f, 1.0f, 0.0f }; - Vector3 p; - Vector3 c = { a.x + (-d.x * height), a.y + (-d.y * height), a.z + (-d.z * height) }; //= a + (-d * h); - Vector3 e0 = VectorPerpendicular(d); - Vector3 e1 = VectorCrossProduct(e0, d); - float angInc = 360.0 / slices * DEG2RAD; - - if (radiusTop == 0) // Draw pyramid or cone - { - //d – axis defined as a normalized vector from base to apex - //a – position of apex (top point) - //h – height - //rd – radius of directrix - //n – number of radial "slices" - - // TODO: Review drawing to use RL_TRIANGLES + rlPushMatrix(); + rlTranslatef(centerPos.x, centerPos.y, centerPos.z); + //rlRotatef(rotation, 0, 1, 0); + rlScalef(radius, radius, radius); - // Draw cone top - rlBegin(GL_TRIANGLE_FAN); + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex3f(a.x, a.y, a.z); - for (int i = 0; i <= slices; i++) + for(int i = 0; i < 2 * rings + 1; i ++) { - float rad = angInc * i; - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); - rlVertex3f(p.x, p.y, p.z); + for(int j = 0; j < slices; j++) + { + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*i)) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*i)) * radius, + cos(DEG2RAD*(270+(90/rings)*i)) * cos(DEG2RAD*(j*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)) * radius); + + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*(j*360/slices)) * radius); + + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*(i+1))) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*(i+1))) * radius, + cos(DEG2RAD*(270+(90/rings)*(i+1))) * cos(DEG2RAD*(j*360/slices)) * radius); + rlVertex3f(cos(DEG2RAD*(270+(90/rings)*i)) * sin(DEG2RAD*(j*360/slices)) * radius, + sin(DEG2RAD*(270+(90/rings)*i)) * radius, + cos(DEG2RAD*(270+(90/rings)*i)) * cos(DEG2RAD*(j*360/slices)) * radius); + } } rlEnd(); - - // Draw cone bottom - rlBegin(GL_TRIANGLE_FAN); + rlPopMatrix(); +} + +// Draw a cylinder +// NOTE: It could be also used for pyramid and cone +void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) +{ + if (sides < 3) sides = 3; + + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); + + rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex3f(c.x, c.y, c.z); - - for (int i = slices; i >= 0; i--) + + if (radiusTop > 0) { - float rad = angInc * i; - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); - rlVertex3f(p.x, p.y, p.z); + // Draw Body ------------------------------------------------------------------------------------- + for(int i = 0; i < 360; i += 360/sides) + { + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); //Bottom Right + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right + + rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); //Top Left + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right + } + + // Draw Cap -------------------------------------------------------------------------------------- + for(int i = 0; i < 360; i += 360/sides) + { + rlVertex3f(0, height, 0); + rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); + } } - rlEnd(); - } - else // Draw cylinder - { - - // TODO: Review drawing to use RL_TRIANGLES - - // Draw cylinder top (pointed cap) - rlBegin(GL_TRIANGLE_FAN); - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex3f(c.x, c.y + height, c.z); - for (int i = slices; i >= 0; i--) + else { - float rad = angInc * i; - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height; - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop); - rlVertex3f(p.x, p.y, p.z); + // Draw Cone ------------------------------------------------------------------------------------- + for(int i = 0; i < 360; i += 360/sides) + { + rlVertex3f(0, height, 0); + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); + } } - rlEnd(); - - // Draw cylinder sides - rlBegin(GL_TRIANGLE_STRIP); - rlColor4ub(color.r, color.g, color.b, color.a); - for (int i = slices; i >= 0; i--) + + // Draw Base ----------------------------------------------------------------------------------------- + for(int i = 0; i < 360; i += 360/sides) { - float rad = angInc * i; - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height; - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop); - rlVertex3f(p.x, p.y, p.z); - - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); - rlVertex3f(p.x, p.y, p.z); + rlVertex3f(0, 0, 0); + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); } - rlEnd(); + rlEnd(); + rlPopMatrix(); +} + +// Draw a wired cylinder +// NOTE: It could be also used for pyramid and cone +void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) +{ + if(sides < 3) sides = 3; + + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); - // Draw cylinder bottom - rlBegin(GL_TRIANGLE_FAN); + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex3f(c.x, c.y, c.z); - for (int i = slices; i >= 0; i--) + + for(int i = 0; i < 360; i += 360/sides) { - float rad = angInc * i; - p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); - p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); - p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); - rlVertex3f(p.x, p.y, p.z); + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); + + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); + + rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); + rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); + + rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); + rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); } rlEnd(); - } -} - -// Draw a cylinder/cone wires -void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color) -{ - // TODO: Draw sphere using RL_LINES! - - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - DrawCylinder(position, radiusTop, radiusBottom, height, slices, color); - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + rlPopMatrix(); } // Draw a plane +// TODO: Test this function void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) { - // TODO: Review vertex translate/rotate/scale mechanism - // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -353,7 +454,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) rlRotatef(rotation.z, 0, 0, 1); rlScalef(size.x, 1.0f, size.y); - rlBegin(GL_QUADS); + rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 1.0f, 0.0f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, -0.5f); @@ -365,6 +466,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) } // Draw a plane with divisions +// TODO: Test this function void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color) { float quadWidth = size.x / slicesX; @@ -373,8 +475,6 @@ void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, float texPieceW = 1 / size.x; float texPieceH = 1 / size.y; - // TODO: Review vertex translate/rotate/scale mechanism - // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); rlTranslatef(-size.x / 2, 0.0f, -size.y / 2); @@ -444,66 +544,116 @@ void DrawGrid(int slices, float spacing) rlEnd(); } -// Draw gizmo (with or without orbits) -void DrawGizmo(Vector3 position, bool orbits) +// Draw gizmo +void DrawGizmo(Vector3 position) { // NOTE: RGB = XYZ float lenght = 1.0f; - float radius = 1.0f; - - //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) - //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) - - // GL_LINE_SMOOTH is very poorly supported on desktop GL. - // A lot of drivers ignore it, so most people avoid using... - + rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); - //glRotatef(rotation, 0, 1, 0); + //rlRotatef(rotation, 0, 1, 0); rlScalef(lenght, lenght, lenght); - rlBegin(GL_LINES); - rlColor3f(1.0f, 0.0f, 0.0f); - rlVertex3f(0.0f, 0.0f, 0.0f); - rlVertex3f(1.0f, 0.0f, 0.0f); + rlBegin(RL_LINES); + rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f); + rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(1.0f, 0.0f, 0.0f); - rlColor3f(0.0f, 1.0f, 0.0f); - rlVertex3f(0.0f, 0.0f, 0.0f); - rlVertex3f(0.0f, 1.0f, 0.0f); + rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f); + rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 1.0f, 0.0f); - rlColor3f(0.0f, 0.0f, 1.0f); - rlVertex3f(0.0f, 0.0f, 0.0f); - rlVertex3f(0.0f, 0.0f, 1.0f); - rlEnd(); + rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 0.0f); + rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 1.0f); + rlEnd(); + rlPopMatrix(); +} + +void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits) +{ + // NOTE: RGB = XYZ + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); + rlScalef(scale, scale, scale); - if (orbits) - { - rlBegin(GL_LINE_LOOP); - rlColor4f(1.0f, 0.0f, 0.0f, 0.4f); - for (int i=0; i < 360; i++) rlVertex3f(sin(DEG2RAD*i) * radius, 0, cos(DEG2RAD*i) * radius); - rlEnd(); - - rlBegin(GL_LINE_LOOP); - rlColor4f(0.0f, 1.0f, 0.0f, 0.4f); - for (int i=0; i < 360; i++) rlVertex3f(sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius, 0); - rlEnd(); - - rlBegin(GL_LINE_LOOP); - rlColor4f(0.0f, 0.0f, 1.0f, 0.4f); - for (int i=0; i < 360; i++) rlVertex3f(0, sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius); - rlEnd(); - } + if(rotation.x) rlRotatef(rotation.x, 1, 0, 0); + if(rotation.y) rlRotatef(rotation.y, 0, 1, 0); + if(rotation.z) rlRotatef(rotation.z, 0, 0, 1); + rlBegin(RL_LINES); + // X Axis + rlColor4ub(200, 0, 0, 255); // RED + rlVertex3f(position.x, position.y, position.z); + rlVertex3f(position.x + 1, position.y, position.z); + + // ArrowX + rlVertex3f(position.x + 1.1, position.y, position.z); + rlVertex3f(position.x + .9, position.y, position.z + .1); + + rlVertex3f(position.x + 1.1, position.y, position.z); + rlVertex3f(position.x + .9, position.y, position.z - .1); + + // Y Axis + rlColor4ub(0, 200, 0, 255); // GREEN + rlVertex3f(position.x, position.y, position.z); + rlVertex3f(position.x, position.y + 1, position.z); + + // ArrowY + rlVertex3f(position.x, position.y + 1.1, position.z); + rlVertex3f(position.x + .1, position.y + .9, position.z); + + rlVertex3f(position.x, position.y + 1.1, position.z); + rlVertex3f(position.x - .1, position.y + .9, position.z); + + // Z Axis + rlColor4ub(0, 0, 200, 255); // BLUE + rlVertex3f(position.x, position.y, position.z); + rlVertex3f(position.x, position.y, position.z - 1); + + // ArrowZ + rlVertex3f(position.x, position.y, position.z - 1.1); + rlVertex3f(position.x + .1, position.y, position.z - .9); + + rlVertex3f(position.x, position.y, position.z - 1.1); + rlVertex3f(position.x - .1, position.y, position.z - .9); + + // Extra + if(orbits) + { + int n = 3; + + // X Axis + rlColor4ub(200, 0, 0, 255); // RED + for (int i=0; i < 360; i++) + { + rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n); + rlVertex3f(0,position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n); + } + + // Y Axis + rlColor4ub(0, 200, 0, 255); // GREEN + for (int i=0; i < 360; i++) + { + rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n); + rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, 0, position.y + cos(DEG2RAD*(i+1)) * scale/n); + } + + // Z Axis + rlColor4ub(0, 0, 200, 255); // BLUE + for (int i=0; i < 360; i++) + { + rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0); + rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n, 0); + } + } + rlEnd(); rlPopMatrix(); - - //glDisable(GL_LINE_SMOOTH); } // Load a 3d model (.OBJ) // TODO: Add comments explaining this function process Model LoadModel(const char *fileName) { - struct Model model; + VertexData vData; char dataType; char comments[200]; @@ -617,17 +767,19 @@ Model LoadModel(const char *fileName) Vector3 midNormals[numNormals]; Vector2 midTexCoords[numTexCoords]; - model.numVertices = numTriangles*3; - - model.vertices = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); - model.normals = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); - model.texcoords = (Vector2 *)malloc(model.numVertices * sizeof(Vector2)); + vData.numVertices = numTriangles*3; + + vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float)); + vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float)); + vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float)); int countVertex = 0; int countNormals = 0; int countTexCoords = 0; - int countMaxVertex = 0; + int vCounter = 0; // Used to count vertices float by float + int tcCounter = 0; // Used to count texcoords float by float + int nCounter = 0; // Used to count normals float by float rewind(objFile); @@ -671,31 +823,58 @@ Model LoadModel(const char *fileName) } break; case 'f': { + // At this point all vertex data (v, vt, vn) have been gathered on midVertices, midTexCoords, midNormals + // Now we can organize that data into our VertexData struct + int vNum, vtNum, vnNum; fscanf(objFile, "%c", &dataType); fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum); - model.vertices[countMaxVertex] = midVertices[vNum-1]; - model.normals[countMaxVertex] = midNormals[vnNum-1]; - model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; - model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; - countMaxVertex++; - + vData.vertices[vCounter] = midVertices[vNum-1].x; + vData.vertices[vCounter + 1] = midVertices[vNum-1].y; + vData.vertices[vCounter + 2] = midVertices[vNum-1].z; + vCounter += 3; + + vData.normals[nCounter] = midNormals[vnNum-1].x; + vData.normals[nCounter + 1] = midNormals[vnNum-1].y; + vData.normals[nCounter + 2] = midNormals[vnNum-1].z; + nCounter += 3; + + vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x; + vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y; + tcCounter += 2; + fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum); - model.vertices[countMaxVertex] = midVertices[vNum-1]; - model.normals[countMaxVertex] = midNormals[vnNum-1]; - model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; - model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; - countMaxVertex++; + vData.vertices[vCounter] = midVertices[vNum-1].x; + vData.vertices[vCounter + 1] = midVertices[vNum-1].y; + vData.vertices[vCounter + 2] = midVertices[vNum-1].z; + vCounter += 3; + + vData.normals[nCounter] = midNormals[vnNum-1].x; + vData.normals[nCounter + 1] = midNormals[vnNum-1].y; + vData.normals[nCounter + 2] = midNormals[vnNum-1].z; + nCounter += 3; + + vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x; + vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y; + tcCounter += 2; fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum); - model.vertices[countMaxVertex] = midVertices[vNum-1]; - model.normals[countMaxVertex] = midNormals[vnNum-1]; - model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; - model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; - countMaxVertex++; + vData.vertices[vCounter] = midVertices[vNum-1].x; + vData.vertices[vCounter + 1] = midVertices[vNum-1].y; + vData.vertices[vCounter + 2] = midVertices[vNum-1].z; + vCounter += 3; + + vData.normals[nCounter] = midNormals[vnNum-1].x; + vData.normals[nCounter + 1] = midNormals[vnNum-1].y; + vData.normals[nCounter + 2] = midNormals[vnNum-1].z; + nCounter += 3; + + vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x; + vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y; + tcCounter += 2; } break; default: break; } @@ -703,10 +882,19 @@ Model LoadModel(const char *fileName) fclose(objFile); -#ifdef USE_OPENGL_33 - - // TODO: Use loaded data to generate VAO + // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct + + Model model; +#ifdef USE_OPENGL_11 + model.data = vData; // model data is vertex data +#else + model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO + + // Now that vertex data is uploaded to GPU, we can free arrays + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); #endif return model; @@ -715,8 +903,8 @@ Model LoadModel(const char *fileName) // Load a heightmap image as a 3d model Model LoadHeightmap(Image heightmap, float maxHeight) { - Model model; - + VertexData vData; + int mapX = heightmap.width; int mapZ = heightmap.height; @@ -724,13 +912,16 @@ Model LoadHeightmap(Image heightmap, float maxHeight) // TODO: Consider resolution when generating model data? int numTriangles = (mapX-1)*(mapZ-1)*2; // One quad every four pixels - model.numVertices = numTriangles*3; + vData.numVertices = numTriangles*3; - model.vertices = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); - model.normals = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); - model.texcoords = (Vector2 *)malloc(model.numVertices * sizeof(Vector2)); + vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float)); + vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float)); + vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float)); + + int vCounter = 0; // Used to count vertices float by float + int tcCounter = 0; // Used to count texcoords float by float + int nCounter = 0; // Used to count normals float by float - int vCounter = 0; int trisCounter = 0; float scaleFactor = maxHeight/255; // TODO: Review scaleFactor calculation @@ -743,62 +934,82 @@ Model LoadHeightmap(Image heightmap, float maxHeight) //---------------------------------------------------------- // one triangle - 3 vertex - model.vertices[vCounter].x = x; - model.vertices[vCounter].y = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor; - model.vertices[vCounter].z = z; + vData.vertices[vCounter] = x; + vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor; + vData.vertices[vCounter + 2] = z; - model.vertices[vCounter+1].x = x; - model.vertices[vCounter+1].y = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor; - model.vertices[vCounter+1].z = z+1; + vData.vertices[vCounter + 3] = x; + vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor; + vData.vertices[vCounter + 5] = z+1; - model.vertices[vCounter+2].x = x+1; - model.vertices[vCounter+2].y = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor; - model.vertices[vCounter+2].z = z; + vData.vertices[vCounter + 6] = x+1; + vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor; + vData.vertices[vCounter + 8] = z; // another triangle - 3 vertex - model.vertices[vCounter+3] = model.vertices[vCounter+2]; - model.vertices[vCounter+4] = model.vertices[vCounter+1]; + vData.vertices[vCounter + 9] = vData.vertices[vCounter + 6]; + vData.vertices[vCounter + 10] = vData.vertices[vCounter + 7]; + vData.vertices[vCounter + 11] = vData.vertices[vCounter + 8]; - model.vertices[vCounter+5].x = x+1; - model.vertices[vCounter+5].y = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor; - model.vertices[vCounter+5].z = z+1; + vData.vertices[vCounter + 12] = vData.vertices[vCounter + 3]; + vData.vertices[vCounter + 13] = vData.vertices[vCounter + 4]; + 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 + 17] = z+1; + vCounter += 18; // 6 vertex, 18 floats // Fill texcoords array with data //-------------------------------------------------------------- - model.texcoords[vCounter].x = (float)x / (mapX-1); - model.texcoords[vCounter].y = (float)z / (mapZ-1); + vData.texcoords[tcCounter] = (float)x / (mapX-1); + vData.texcoords[tcCounter + 1] = (float)z / (mapZ-1); + + vData.texcoords[tcCounter + 2] = (float)x / (mapX-1); + vData.texcoords[tcCounter + 3] = (float)(z+1) / (mapZ-1); - model.texcoords[vCounter+1].x = (float)x / (mapX-1); - model.texcoords[vCounter+1].y = (float)(z+1) / (mapZ-1); + vData.texcoords[tcCounter + 4] = (float)(x+1) / (mapX-1); + vData.texcoords[tcCounter + 5] = (float)z / (mapZ-1); - model.texcoords[vCounter+2].x = (float)(x+1) / (mapX-1); - model.texcoords[vCounter+2].y = (float)z / (mapZ-1); + vData.texcoords[tcCounter + 6] = vData.texcoords[tcCounter + 4]; + vData.texcoords[tcCounter + 7] = vData.texcoords[tcCounter + 5]; - model.texcoords[vCounter+3] = model.texcoords[vCounter+2]; - model.texcoords[vCounter+4] = model.texcoords[vCounter+1]; + vData.texcoords[tcCounter + 8] = vData.texcoords[tcCounter + 2]; + vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 1]; - model.texcoords[vCounter+5].x = (float)(x+1) / (mapX-1); - model.texcoords[vCounter+5].y = (float)(z+1) / (mapZ-1); + vData.texcoords[tcCounter + 10] = (float)(x+1) / (mapX-1); + vData.texcoords[tcCounter + 11] = (float)(z+1) / (mapZ-1); + tcCounter += 12; // 6 texcoords, 12 floats // Fill normals array with data //-------------------------------------------------------------- // TODO: Review normals calculation - model.normals[vCounter] = (Vector3){ 0.0f, 1.0f, 0.0f }; - model.normals[vCounter+1] = (Vector3){ 0.0f, 1.0f, 0.0f }; - model.normals[vCounter+2] = (Vector3){ 0.0f, 1.0f, 0.0f }; - model.normals[vCounter+3] = (Vector3){ 0.0f, 1.0f, 0.0f }; - model.normals[vCounter+4] = (Vector3){ 0.0f, 1.0f, 0.0f }; - model.normals[vCounter+5] = (Vector3){ 0.0f, 1.0f, 0.0f }; - - vCounter += 6; + for (int i = 0; i < 18; i += 3) + { + vData.normals[nCounter + i] = 0.0f; + vData.normals[nCounter + i + 1] = 1.0f; + vData.normals[nCounter + i + 2] = 0.0f; + } + + nCounter += 18; // 6 vertex, 18 floats + trisCounter += 2; } } -#ifdef USE_OPENGL_33 - - // TODO: Use loaded data to generate VAO + // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct + + Model model; + +#ifdef USE_OPENGL_11 + model.data = vData; // model data is vertex data +#else + model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO + // Now that vertex data is uploaded to GPU, we can free arrays + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); #endif return model; @@ -807,62 +1018,21 @@ Model LoadHeightmap(Image heightmap, float maxHeight) // Unload 3d model from memory void UnloadModel(Model model) { - free(model.vertices); - free(model.texcoords); - free(model.normals); +#ifdef USE_OPENGL_11 + free(model.data.vertices); + free(model.data.texcoords); + free(model.data.normals); +#endif + +#ifdef USE_OPENGL_33 + rlDeleteVertexArrays(model.vaoId); +#endif } // Draw a model void DrawModel(Model model, Vector3 position, float scale, Color color) { - // NOTE: For models we use Vertex Arrays (OpenGL 1.1) - //static int rotation = 0; - - // NOTE: Add OpenGL 3.3+ VAOs-based drawing! --> Move this stuff to rlgl? - - glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array - glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array - glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array - - glVertexPointer(3, GL_FLOAT, 0, model.vertices); // Pointer to vertex coords array - glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords); // Pointer to texture coords array - glNormalPointer(GL_FLOAT, 0, model.normals); // Pointer to normals array - //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED) - - rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - //glRotatef(rotation * GetFrameTime(), 0, 1, 0); - rlScalef(scale, scale, scale); - - rlColor4ub(color.r, color.g, color.b, color.a); - - glDrawArrays(GL_TRIANGLES, 0, model.numVertices); - rlPopMatrix(); - - glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array - glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array - glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array - - //rotation += 10; - -// Model drawing in OpenGL 3.3+, transform is passed to shader -/* - glUseProgram(shaderProgram); // Use our shader - - Matrix modelview = MatrixMultiply(model.transform, view); - - glUniformMatrix4fv(projectionMatrixLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelview)); - glUniform1i(textureLoc, 0); - - glBindVertexArray(model.vaoId); - glBindTexture(GL_TEXTURE_2D, model.texId); - - glDrawArrays(GL_TRIANGLES, 0, model.numVertices); - - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - glBindVertexArray(0); // Unbind VAO -*/ + rlglDrawModel(model, false); } // Draw a textured model @@ -878,11 +1048,7 @@ void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, // Draw a model wires void DrawModelWires(Model model, Vector3 position, float scale, Color color) { - // TODO: Draw model using RL_LINES... or look for a way to deal with polygon mode! - - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - DrawModel(model, position, scale, color); - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + rlglDrawModel(model, true); } // Draw a billboard diff --git a/src/raylib.h b/src/raylib.h index 4534c5ec..450d2e6f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -355,15 +355,17 @@ const char *FormatText(const char *text, ...); void DrawCube(Vector3 position, float width, float height, float lenght, Color color); // Draw cube void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color); // Draw cube wires +void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float lenght, Color color); // Draw cube textured void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters -void DrawSphereWires(Vector3 centerPos, float radius, Color color); // Draw sphere wires +void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) -void DrawGizmo(Vector3 position, bool orbits); // Draw gizmo (with or without orbits) +void DrawGizmo(Vector3 position); // Draw simple gizmo +void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits); // Draw gizmo with extended parameters //DrawTorus(), DrawTeapot() are useless... //------------------------------------------------------------------------------------ diff --git a/src/rlgl.c b/src/rlgl.c index 63ed9ec4..e25e72be 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -49,9 +49,9 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MATRIX_STACK_SIZE 16 // TODO: REVIEW: Matrix stack required? +#define MATRIX_STACK_SIZE 16 // Matrix stack max size #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes -#define TEMP_VERTEX_BUFFER_SIZE 1024 // Temporal Vertex Buffer (required for post-transformations) +#define TEMP_VERTEX_BUFFER_SIZE 1024 // Temporal Vertex Buffer (required for vertex-transformations) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -67,7 +67,7 @@ typedef struct { float *vertices; // 3 components per vertex float *colors; // 4 components per vertex } VertexPositionColorBuffer; - +/* typedef struct { int vCounter; int tcCounter; @@ -76,7 +76,17 @@ typedef struct { float *texcoords; // 2 components per vertex float *colors; // 4 components per vertex } VertexPositionColorTextureBuffer; - +*/ +/* +typedef struct { + int vCounter; + int tcCounter; + int nCounter; + float *vertices; // 3 components per vertex + float *texcoords; // 2 components per vertex + float *normals; // 3 components per vertex +} VertexPositionTextureNormalBuffer; +*/ typedef struct { int vCounter; int tcCounter; @@ -108,8 +118,8 @@ static int currentMatrixMode; static DrawMode currentDrawMode; // Vertex arrays for lines, triangles and quads -static VertexPositionColorBuffer lines; -static VertexPositionColorTextureBuffer triangles; +static VertexPositionColorBuffer lines; // No texture support +static VertexPositionColorBuffer triangles; // No texture support static VertexPositionColorTextureIndexBuffer quads; // Vetex-Fragment Shader Program ID @@ -614,6 +624,14 @@ void rlDeleteTextures(unsigned int id) glDeleteTextures(1, &id); } +// Unload vertex data from GPU memory +void rlDeleteVertexArrays(unsigned int id) +{ +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) + glDeleteVertexArrays(1, &id); +#endif +} + // Clear color buffer with color void rlClearColor(byte r, byte g, byte b, byte a) { @@ -655,10 +673,11 @@ void rlglInit() if (glewIsSupported("GL_VERSION_3_3")) printf("OpenGL 3.3 initialized\n"); /* + // TODO: GLEW is a big library that loads ALL extensions, maybe using glad we can only load required ones... if (!gladLoadGL()) { - printf("Something went wrong!\n"); - exit(-1); + fprintf(stderr, printf("Failed to initialize glad.\n"); + exit(1); } */ // Set default draw mode @@ -694,7 +713,7 @@ void rlglInit() printf("Vendor: %s\n", glGetString(GL_VENDOR)); printf("Renderer: %s\n", glGetString(GL_RENDERER)); printf("Version: %s\n", glGetString(GL_VERSION)); - printf("GLSL: %s\n\n", glGetString(0x8B8C)); // GL_SHADING_LANGUAGE_VERSION + printf("GLSL: %s\n\n", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION InitializeBuffers(); // Init vertex arrays InitializeVAOs(); // Init VBO and VAO @@ -707,7 +726,7 @@ void rlglInit() // Create default white texture for plain colors (required by shader) unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglTexture(1, 1, pixels); + whiteTexture = rlglLoadTexture(1, 1, pixels); // Init draw calls tracking system draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); @@ -794,8 +813,12 @@ void rlglDraw() if (triangles.vCounter > 0) { + glBindTexture(GL_TEXTURE_2D, whiteTexture); + glBindVertexArray(vaoTriangles); glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); + + glBindTexture(GL_TEXTURE_2D, 0); } if (quads.vCounter > 0) @@ -830,8 +853,6 @@ void rlglDraw() glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset)); indicesOffset += draws[i].vCount/4*6; - - //printf("-------Next vertex offset: %i\n", indicesOffset/6*4); } } @@ -849,7 +870,7 @@ void rlglDraw() lines.cCounter = 0; triangles.vCounter = 0; - triangles.vCounter = 0; + triangles.cCounter = 0; quads.vCounter = 0; quads.tcCounter = 0; @@ -861,6 +882,58 @@ void rlglDraw() #endif } +void rlglDrawModel(Model model, bool wires) +{ + if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + +#ifdef USE_OPENGL_11 + // NOTE: For models we use Vertex Arrays (OpenGL 1.1) + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + + glVertexPointer(3, GL_FLOAT, 0, model.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords); // Pointer to texture coords array + glNormalPointer(GL_FLOAT, 0, model.normals); // Pointer to normals array + //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED) + + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); + //glRotatef(rotation * GetFrameTime(), 0, 1, 0); + rlScalef(scale, scale, scale); + + rlColor4ub(color.r, color.g, color.b, color.a); + + glDrawArrays(GL_TRIANGLES, 0, model.numVertices); + rlPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array +#endif + +#ifdef USE_OPENGL_33 + glUseProgram(shaderProgram); // Use our shader + + Matrix modelview2 = MatrixMultiply(model.transform, modelview); + + // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader + glUniformMatrix4fv(projectionMatrixLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelview2)); + glUniform1i(textureLoc, 0); + + glBindVertexArray(model.vaoId); + //glBindTexture(GL_TEXTURE_2D, model.textureId); + + glDrawArrays(GL_TRIANGLES, 0, model.numVertices); + + //glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + glBindVertexArray(0); // Unbind VAO +#endif + + if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + #endif // Initialize Graphics Device (OpenGL stuff) @@ -909,7 +982,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) // Convert image data to OpenGL texture (returns OpenGL valid Id) // NOTE: Image is not unloaded, it should be done manually... -unsigned int rlglTexture(int width, int height, unsigned char *pixels) +unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels) { glBindTexture(GL_TEXTURE_2D,0); // Free any old binding @@ -926,7 +999,7 @@ unsigned int rlglTexture(int width, int height, unsigned char *pixels) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR #ifdef USE_OPENGL_33 - // Trilinear filtering! + // Trilinear filtering //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) //glGenerateMipmap(GL_TEXTURE_2D); // OpenGL 3.3! @@ -947,6 +1020,39 @@ unsigned int rlglTexture(int width, int height, unsigned char *pixels) return id; } +#ifdef USE_OPENGL_33 +unsigned int rlglLoadModel(VertexData data) +{ + GLuint vaoModel; // Vertex Array Objects (VAO) + GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) + + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &vaoModel); + glBindVertexArray(vaoModel); + + // Create buffers for our vertex data (positions, texcoords, normals) + glGenBuffers(3, vertexBuffer); + + // Enable vertex attributes + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(vertexLoc); + glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*data.numVertices, data.texcoords, GL_STATIC_DRAW); + glEnableVertexAttribArray(texcoordLoc); + glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.normals, GL_STATIC_DRAW); + //glEnableVertexAttribArray(normalLoc); + //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0); + + return vaoModel; +} +#endif + // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { @@ -1303,11 +1409,13 @@ static void UpdateBuffers() // Triangles - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*triangles.vCounter, triangles.vertices); + // Triangles - colors buffer glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*triangles.cCounter, triangles.colors); //-------------------------------------------------------------- @@ -1355,6 +1463,12 @@ static void UpdateBuffers() glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*quads.vCounter, quads.colors); } + + // Another option would be using buffer mapping... + //triangles.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); + //-------------------------------------------------------------- // Unbind the current VAO diff --git a/src/rlgl.h b/src/rlgl.h index b4033050..174310dd 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -29,9 +29,15 @@ #ifndef RLGL_H #define RLGL_H +//#define RLGL_STANDALONE // NOTE: To use rlgl as standalone lib, just uncomment this line + +#ifndef RLGL_STANDALONE + #include "raylib.h" // Required for typedef: Model +#endif + // Select desired OpenGL version -//#define USE_OPENGL_11 -#define USE_OPENGL_33 +#define USE_OPENGL_11 +//#define USE_OPENGL_33 //#define USE_OPENGL_ES2 //---------------------------------------------------------------------------------- @@ -50,6 +56,29 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode; typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; +#ifdef RLGL_STANDALONE + typedef struct Model Model; +#endif + +typedef struct { + int numVertices; + float *vertices; // 3 components per vertex + float *texcoords; // 2 components per vertex + float *normals; // 3 components per vertex +} VertexData; + +#ifdef USE_OPENGL_11 +struct Model { + VertexData data; +}; +#else +struct Model { + unsigned int vaoId; + Matrix transform; + int numVertices; +}; +#endif + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -88,7 +117,8 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) //------------------------------------------------------------------------------------ void rlEnableTexture(unsigned int id); // Enable texture usage void rlDisableTexture(); // Disable texture usage -void rlDeleteTextures(unsigned int id); // Delete OpenGL texture +void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU +void rlDeleteVertexArrays(unsigned int id); // Unload vertex data from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(); // Clear used screen buffers (color and depth) @@ -96,13 +126,15 @@ void rlClearScreenBuffers(); // Clear used screen buffers (color // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ #ifdef USE_OPENGL_33 -void rlglInit(); // Initialize rlgl (shaders, VAO, VBO...) -void rlglClose(); // De-init rlgl -void rlglDraw(); // Draw VAOs +void rlglInit(); // Initialize rlgl (shaders, VAO, VBO...) +void rlglClose(); // De-init rlgl +void rlglDraw(); // Draw VAOs +unsigned int rlglLoadModel(VertexData data); #endif +void rlglDrawModel(Model model, bool wires); // Draw model void rlglInitGraphicsDevice(int fbWidth, int fbHeight); // Initialize Graphics Device (OpenGL stuff) -unsigned int rlglTexture(int width, int height, unsigned char *pixels); // Create OpenGL texture +unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels); // Load in GPU OpenGL texture byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) #ifdef USE_OPENGL_33 diff --git a/src/shapes.c b/src/shapes.c index 838a9efa..42496351 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -62,7 +62,7 @@ void DrawPixel(int posX, int posY, Color color) rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2i(posX, posY); rlVertex2i(posX + 1, posY + 1); - rlEnd(); + rlEnd(); } // Draw a pixel (Vector version) @@ -96,7 +96,6 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) } // Draw a color-filled circle -// TODO: Review, on some GPUs is drawn with a weird transparency (GL_POLYGON_SMOOTH issue?) void DrawCircle(int centerX, int centerY, float radius, Color color) { DrawPoly((Vector2){centerX, centerY}, 360, radius, 0, color); @@ -107,13 +106,13 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) { rlBegin(RL_TRIANGLES); - for (int i=0; i <= 360; i += 2) //i++ --> Step = 1.0 pixels + for (int i=0; i < 360; i += 2) { rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(centerX, centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius); - rlVertex2f(centerX + sin(DEG2RAD*(i+1)) * radius, centerY + cos(DEG2RAD*(i+1)) * radius); + rlVertex2f(centerX + sin(DEG2RAD*(i+2)) * radius, centerY + cos(DEG2RAD*(i+2)) * radius); } rlEnd(); } @@ -127,7 +126,7 @@ void DrawCircleV(Vector2 center, float radius, Color color) rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2i(center.x, center.y); rlVertex2f(center.x + sin(DEG2RAD*i) * radius, center.y + cos(DEG2RAD*i) * radius); - rlVertex2f(center.x + sin(DEG2RAD*(i+1)) * radius, center.y + cos(DEG2RAD*(i+1)) * radius); + rlVertex2f(center.x + sin(DEG2RAD*(i+2)) * radius, center.y + cos(DEG2RAD*(i+2)) * radius); } rlEnd(); } @@ -135,9 +134,6 @@ void DrawCircleV(Vector2 center, float radius, Color color) // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { - //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) - //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) - rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -148,10 +144,6 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) rlVertex2f(centerX + sin(DEG2RAD*(i+1)) * radius, centerY + cos(DEG2RAD*(i+1)) * radius); } rlEnd(); - - //glDisable(GL_LINE_SMOOTH); - - // TODO: Draw all lines with line smooth??? --> Do it before drawing lines VAO } // Draw a color-filled rectangle @@ -255,16 +247,17 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col if (sides < 3) sides = 3; rlPushMatrix(); - - rlRotatef(rotation, 0, 0, 1); // TODO: compute vertex rotation manually! + rlTranslatef(center.x, center.y, 0.0); + rlRotatef(rotation, 0, 0, 1); rlBegin(RL_TRIANGLES); - for (int i=0; i < 360; i += 2) + for (int i=0; i < 360; i += 360/sides) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2i(center.x, center.y); - rlVertex2f(center.x + sin(DEG2RAD*i) * radius, center.y + cos(DEG2RAD*i) * radius); - rlVertex2f(center.x + sin(DEG2RAD*(i+1)) * radius, center.y + cos(DEG2RAD*(i+1)) * radius); + + rlVertex2i(0, 0); + rlVertex2f(sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius); + rlVertex2f(sin(DEG2RAD*(i+360/sides)) * radius, cos(DEG2RAD*(i+360/sides)) * radius); } rlEnd(); rlPopMatrix(); diff --git a/src/text.c b/src/text.c index b897f1f3..2d75a6d9 100644 --- a/src/text.c +++ b/src/text.c @@ -313,7 +313,6 @@ void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int f rlEnableTexture(spriteFont.texture.glId); - // Optimized to use one draw call per string rlBegin(RL_QUADS); for(int i = 0; i < length; i++) { diff --git a/src/textures.c b/src/textures.c index f8402b0c..75300eac 100644 --- a/src/textures.c +++ b/src/textures.c @@ -273,31 +273,30 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc { rlEnableTexture(texture.glId); - // TODO: Apply rotation to vertex! --> rotate from origin CW (0, 0) - // TODO: Compute vertex scaling! - // NOTE: Rotation is applied before translation and scaling, even being called in inverse order... - // NOTE: Rotation point is upper-left corner - //rlTranslatef(position.x, position.y, 0); - //rlScalef(scale, scale, 1.0f); - //rlRotatef(rotation, 0, 0, 1); - - rlBegin(RL_QUADS); - rlColor4ub(tint.r, tint.g, tint.b, tint.a); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer - - rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(position.x, position.y); // Bottom-left corner for texture and quad - - rlTexCoord2f(0.0f, 1.0f); - rlVertex2f(position.x, position.y + texture.height); // Bottom-right corner for texture and quad - - rlTexCoord2f(1.0f, 1.0f); - rlVertex2f(position.x + texture.width, position.y + texture.height); // Top-right corner for texture and quad - - rlTexCoord2f(1.0f, 0.0f); - rlVertex2f(position.x + texture.width, position.y); // Top-left corner for texture and quad - rlEnd(); + // NOTE: Rotation point is upper-left corner + rlPushMatrix(); + rlTranslatef(position.x, position.y, 0.0); + rlScalef(scale, scale, 1.0f); + rlRotatef(rotation, 0, 0, 1); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(position.x, position.y); // Bottom-left corner for texture and quad + + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(position.x, position.y + texture.height); // Bottom-right corner for texture and quad + + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f(position.x + texture.width, position.y + texture.height); // Top-right corner for texture and quad + + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f(position.x + texture.width, position.y); // Top-left corner for texture and quad + rlEnd(); + rlPopMatrix(); rlDisableTexture(); } @@ -337,32 +336,33 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V { rlEnableTexture(texture.glId); - // TODO: Apply translation, rotation and scaling of vertex manually! - - //rlTranslatef(-origin.x, -origin.y, 0); - //rlRotatef(rotation, 0, 0, 1); - //rlTranslatef(destRec.x + origin.x, destRec.y + origin.y, 0); - - rlBegin(RL_QUADS); - rlColor4ub(tint.r, tint.g, tint.b, tint.a); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer - - // Bottom-left corner for texture and quad - rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); - rlVertex2f(0.0f, 0.0f); - - // Bottom-right corner for texture and quad - rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); - rlVertex2f(destRec.width, 0.0f); - - // Top-right corner for texture and quad - rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); - rlVertex2f(destRec.width, destRec.height); - - // Top-left corner for texture and quad - rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); - rlVertex2f(0.0f, destRec.height); - rlEnd(); + // NOTE: First we translate texture to origin to apply rotation and translation from there + rlPushMatrix(); + rlTranslatef(-origin.x, -origin.y, 0); + rlRotatef(rotation, 0, 0, 1); + rlTranslatef(destRec.x + origin.x, destRec.y + origin.y, 0); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + // Bottom-left corner for texture and quad + rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); + rlVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); + rlVertex2f(destRec.width, 0.0f); + + // Top-right corner for texture and quad + rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); + rlVertex2f(destRec.width, destRec.height); + + // Top-left corner for texture and quad + rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); + rlVertex2f(0.0f, destRec.height); + rlEnd(); + rlPopMatrix(); rlDisableTexture(); } @@ -385,7 +385,7 @@ Texture2D CreateTexture(Image image) j++; } - texture.glId = rlglTexture(image.width, image.height, img); + texture.glId = rlglLoadTexture(image.width, image.height, img); texture.width = image.width; texture.height = image.height; -- cgit v1.2.3 From e6b82cb111c3485c5e6131fe29791f938305bce3 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 9 Apr 2014 20:25:26 +0200 Subject: Lots of changes, most of them under testing-review Added a Tracing/Log system Added OGG stream music support (DOESN'T WORK) Added Compressed textures support * This update is probably very buggy... --- src/audio.c | 609 +++++++++++++++++++++++++++++++++++++------------------ src/core.c | 88 ++++---- src/models.c | 82 ++++---- src/raylib.h | 78 ++++--- src/rlgl.c | 198 +++++++++++++----- src/rlgl.h | 18 +- src/stb_vorbis.c | 4 + src/stb_vorbis.h | 9 +- src/text.c | 34 ++-- src/textures.c | 387 ++++++++++++++++++++++++----------- src/utils.c | 148 ++++++++++++-- src/utils.h | 9 +- 12 files changed, 1141 insertions(+), 523 deletions(-) (limited to 'src/text.c') diff --git a/src/audio.c b/src/audio.c index ac6fc2b3..35874b59 100644 --- a/src/audio.c +++ b/src/audio.c @@ -37,7 +37,7 @@ #include "utils.h" // rRES data decompression utility function -//#include "stb_vorbis.h" // TODO: OGG loading functions +//#include "stb_vorbis.h" // OGG loading functions //---------------------------------------------------------------------------------- // Defines and Macros @@ -47,6 +47,29 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- +// Sound source type (all file loaded in memory) +/* +struct Sound { + unsigned int source; + unsigned int buffer; +}; + +// Music type (file streamming from memory) +// NOTE: Anything longer than ~10 seconds should be Music... +struct Music { + stb_vorbis* stream; + stb_vorbis_info info; + + ALuint id; + ALuint buffers[2]; + ALuint source; + ALenum format; + + int bufferSize; + int totalSamplesLeft; + bool loop; +}; +*/ // Wave file data typedef struct Wave { @@ -60,7 +83,8 @@ typedef struct Wave { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -// Nop... +static bool musicIsPlaying; +static Music *currentMusic; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -68,6 +92,10 @@ typedef struct Wave { static Wave LoadWAV(char *fileName); static void UnloadWAV(Wave wave); //static Ogg LoadOGG(char *fileName); +static bool MusicStream(Music music, ALuint buffer); + +extern bool MusicStreamUpdate(); +extern void PlayCurrentMusic(); //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions @@ -79,30 +107,27 @@ void InitAudioDevice() // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); - if(!device) - { - fprintf(stderr, "Could not open a device!\n"); - exit(1); - } + if(!device) TraceLog(ERROR, "Could not open audio device"); ALCcontext *context = alcCreateContext(device, NULL); if(context == NULL || alcMakeContextCurrent(context) == ALC_FALSE) { - if(context != NULL) alcDestroyContext(context); + if(context != NULL) alcDestroyContext(context); alcCloseDevice(device); - fprintf(stderr, "Could not set a context!\n"); - exit(1); + TraceLog(ERROR, "Could not setup audio context"); } - printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + TraceLog(INFO, "Audio device and context initialized: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); // Listener definition (just for 2D) alListener3f(AL_POSITION, 0, 0, 0); alListener3f(AL_VELOCITY, 0, 0, 0); alListener3f(AL_ORIENTATION, 0, 0, -1); + + musicIsPlaying = false; } // Close the audio device for the current context, and destroys the context @@ -111,7 +136,7 @@ void CloseAudioDevice() ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); - if (context == NULL) return; + if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing"); device = alcGetContextsDevice(context); @@ -169,10 +194,8 @@ Sound LoadSound(char *fileName) // Unallocate WAV data UnloadWAV(wave); - printf("Sample rate: %i\n", wave.sampleRate); - printf("Channels: %i\n", wave.channels); - - printf("Audio file loaded...!\n"); + TraceLog(INFO, "[%s] Sound file loaded successfully", fileName); + TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels); sound.source = source; sound.buffer = buffer; @@ -196,139 +219,136 @@ Sound LoadSoundFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (!rresFile) printf("Error opening raylib Resource file\n"); - - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) + if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName); + else { - printf("This is not a valid raylib Resource file!\n"); - exit(1); - } - - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); + // Read rres file (basic file check - id) + fread(&id[0], sizeof(char), 1, rresFile); + fread(&id[1], sizeof(char), 1, rresFile); + fread(&id[2], sizeof(char), 1, rresFile); + fread(&id[3], sizeof(char), 1, rresFile); + fread(&version, sizeof(char), 1, rresFile); + fread(&useless, sizeof(char), 1, rresFile); - if (infoHeader.id == resId) + if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) { - found = true; - - // Check data is of valid SOUND type - if (infoHeader.type == 1) // SOUND data type - { - // TODO: Check data compression type - // NOTE: We suppose compression type 2 (DEFLATE - default) - - // Reading SOUND parameters - Wave wave; - short sampleRate, bps; - char channels, reserved; + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); + } + else + { + // Read number of resources embedded + fread(&numRes, sizeof(short), 1, rresFile); - fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency) - fread(&bps, sizeof(short), 1, rresFile); // Bits per sample - fread(&channels, 1, 1, rresFile); // Channels (1 - mono, 2 - stereo) - fread(&reserved, 1, 1, rresFile); // - - printf("Sample rate: %i\n", (int)sampleRate); - printf("Bits per sample: %i\n", (int)bps); - printf("Channels: %i\n", (int)channels); - - wave.sampleRate = sampleRate; - wave.dataSize = infoHeader.srcSize; - wave.bitsPerSample = bps; - wave.channels = (short)channels; - - unsigned char *data = malloc(infoHeader.size); - - fread(data, infoHeader.size, 1, rresFile); - - wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize); - - free(data); - - // Convert wave to Sound (OpenAL) - ALenum format = 0; + for (int i = 0; i < numRes; i++) + { + fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - // The OpenAL format is worked out by looking at the number of channels and the bits per sample - if (wave.channels == 1) + if (infoHeader.id == resId) { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; - } - else if (wave.channels == 2) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; + found = true; + + // Check data is of valid SOUND type + if (infoHeader.type == 1) // SOUND data type + { + // TODO: Check data compression type + // NOTE: We suppose compression type 2 (DEFLATE - default) + + // Reading SOUND parameters + Wave wave; + short sampleRate, bps; + char channels, reserved; + + fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency) + fread(&bps, sizeof(short), 1, rresFile); // Bits per sample + fread(&channels, 1, 1, rresFile); // Channels (1 - mono, 2 - stereo) + fread(&reserved, 1, 1, rresFile); // + + wave.sampleRate = sampleRate; + wave.dataSize = infoHeader.srcSize; + wave.bitsPerSample = bps; + wave.channels = (short)channels; + + unsigned char *data = malloc(infoHeader.size); + + fread(data, infoHeader.size, 1, rresFile); + + wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize); + + free(data); + + // Convert wave to Sound (OpenAL) + ALenum format = 0; + + // The OpenAL format is worked out by looking at the number of channels and the bits per sample + if (wave.channels == 1) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; + } + else if (wave.channels == 2) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; + } + + + // Create an audio source + ALuint source; + alGenSources(1, &source); // Generate pointer to audio source + + alSourcef(source, AL_PITCH, 1); + alSourcef(source, AL_GAIN, 1); + alSource3f(source, AL_POSITION, 0, 0, 0); + alSource3f(source, AL_VELOCITY, 0, 0, 0); + alSourcei(source, AL_LOOPING, AL_FALSE); + + // Convert loaded data to OpenAL buffer + //---------------------------------------- + ALuint buffer; + alGenBuffers(1, &buffer); // Generate pointer to buffer + + // Upload sound data to buffer + alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate); + + // Attach sound buffer to source + alSourcei(source, AL_BUFFER, buffer); + + // Unallocate WAV data + UnloadWAV(wave); + + TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate); + + sound.source = source; + sound.buffer = buffer; + } + else + { + TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName); + } } - - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source - - alSourcef(source, AL_PITCH, 1); - alSourcef(source, AL_GAIN, 1); - alSource3f(source, AL_POSITION, 0, 0, 0); - alSource3f(source, AL_VELOCITY, 0, 0, 0); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - // Upload sound data to buffer - alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - // Unallocate WAV data - UnloadWAV(wave); - - printf("Audio file loaded...!\n"); - - sound.source = source; - sound.buffer = buffer; - } - else - { - - printf("Required resource do not seem to be a valid IMAGE resource\n"); - exit(2); + else + { + // Depending on type, skip the right amount of parameters + switch (infoHeader.type) + { + case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters + case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters + case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) + case 3: break; // TEXT: No parameters + case 4: break; // RAW: No parameters + default: break; + } + + // Jump DATA to read next infoHeader + fseek(rresFile, infoHeader.size, SEEK_CUR); + } } } - else - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } + + fclose(rresFile); } - fclose(rresFile); - - if (!found) printf("Required resource id could not be found in the raylib Resource file!\n"); + if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); return sound; } @@ -345,7 +365,7 @@ void PlaySound(Sound sound) { alSourcePlay(sound.source); // Play the sound - printf("Playing sound!\n"); + TraceLog(INFO, "Playing sound"); // Find the current position of the sound being played // NOTE: Only work when the entire file is in a single buffer @@ -390,7 +410,7 @@ void StopSound(Sound sound) } // Check if a sound is playing -bool IsPlaying(Sound sound) +bool SoundIsPlaying(Sound sound) { bool playing = false; ALint state; @@ -401,6 +421,16 @@ bool IsPlaying(Sound sound) return playing; } +// Check if music is playing +bool MusicIsPlaying(Music music) +{ + ALenum state; + + alGetSourcei(music.source, AL_SOURCE_STATE, &state); + + return (state == AL_PLAYING); +} + // Set volume for a sound void SetVolume(Sound sound, float volume) { @@ -450,61 +480,65 @@ static Wave LoadWAV(char *fileName) if (!wavFile) { - printf("Could not open WAV file.\n"); - exit(1); + TraceLog(WARNING, "[%s] Could not open WAV file", fileName); } - - // Read in the first chunk into the struct - fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); - - // Check for RIFF and WAVE tags - if ((riffHeader.chunkID[0] != 'R' || - riffHeader.chunkID[1] != 'I' || - riffHeader.chunkID[2] != 'F' || - riffHeader.chunkID[3] != 'F') || - (riffHeader.format[0] != 'W' || - riffHeader.format[1] != 'A' || - riffHeader.format[2] != 'V' || - riffHeader.format[3] != 'E')) - printf("Invalid RIFF or WAVE Header"); - - // Read in the 2nd chunk for the wave info - fread(&waveFormat, sizeof(WaveFormat), 1, wavFile); - - // Check for fmt tag - if (waveFormat.subChunkID[0] != 'f' || - waveFormat.subChunkID[1] != 'm' || - waveFormat.subChunkID[2] != 't' || - waveFormat.subChunkID[3] != ' ') - printf("Invalid Wave Format"); - - // Check for extra parameters; - if (waveFormat.subChunkSize > 16) - fseek(wavFile, sizeof(short), SEEK_CUR); - - // Read in the the last byte of data before the sound file - fread(&waveData, sizeof(WaveData), 1, wavFile); - - // Check for data tag - if (waveData.subChunkID[0] != 'd' || - waveData.subChunkID[1] != 'a' || - waveData.subChunkID[2] != 't' || - waveData.subChunkID[3] != 'a') - printf("Invalid data header"); - - // Allocate memory for data - wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); - - // Read in the sound data into the soundData variable - fread(wave.data, waveData.subChunkSize, 1, wavFile); - - // Now we set the variables that we need later - wave.dataSize = waveData.subChunkSize; - wave.sampleRate = waveFormat.sampleRate; - wave.channels = waveFormat.numChannels; - wave.bitsPerSample = waveFormat.bitsPerSample; + else + { + // Read in the first chunk into the struct + fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); + + // Check for RIFF and WAVE tags + if (((riffHeader.chunkID[0] != 'R') || (riffHeader.chunkID[1] != 'I') || (riffHeader.chunkID[2] != 'F') || (riffHeader.chunkID[3] != 'F')) || + ((riffHeader.format[0] != 'W') || (riffHeader.format[1] != 'A') || (riffHeader.format[2] != 'V') || (riffHeader.format[3] != 'E'))) + { + TraceLog(WARNING, "[%s] Invalid RIFF or WAVE Header", fileName); + } + else + { + // Read in the 2nd chunk for the wave info + fread(&waveFormat, sizeof(WaveFormat), 1, wavFile); + + // Check for fmt tag + if ((waveFormat.subChunkID[0] != 'f') || (waveFormat.subChunkID[1] != 'm') || + (waveFormat.subChunkID[2] != 't') || (waveFormat.subChunkID[3] != ' ')) + { + TraceLog(WARNING, "[%s] Invalid Wave format", fileName); + } + else + { + // Check for extra parameters; + if (waveFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR); + + // Read in the the last byte of data before the sound file + fread(&waveData, sizeof(WaveData), 1, wavFile); + + // Check for data tag + if ((waveData.subChunkID[0] != 'd') || (waveData.subChunkID[1] != 'a') || + (waveData.subChunkID[2] != 't') || (waveData.subChunkID[3] != 'a')) + { + TraceLog(WARNING, "[%s] Invalid data header", fileName); + } + else + { + // Allocate memory for data + wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); + + // Read in the sound data into the soundData variable + fread(wave.data, waveData.subChunkSize, 1, wavFile); + + // Now we set the variables that we need later + wave.dataSize = waveData.subChunkSize; + wave.sampleRate = waveFormat.sampleRate; + wave.channels = waveFormat.numChannels; + wave.bitsPerSample = waveFormat.bitsPerSample; + + TraceLog(INFO, "[%s] Wave file loaded successfully", fileName); + } + } + } - fclose(wavFile); + fclose(wavFile); + } return wave; } @@ -516,5 +550,192 @@ static void UnloadWAV(Wave wave) } // TODO: Ogg data loading -//static Ogg LoadOGG(char *fileName) { } +Music LoadMusic(char *fileName) +{ + Music music; + + // Open audio stream + music.stream = stb_vorbis_open_filename(fileName, NULL, NULL); + + if (music.stream == NULL) TraceLog(WARNING, "Could not open ogg audio file"); + else + { + // Get file info + music.info = stb_vorbis_get_info(music.stream); + + printf("Ogg sample rate: %i\n", music.info.sample_rate); + printf("Ogg channels: %i\n", music.info.channels); + printf("Temp memory required: %i\n", music.info.temp_memory_required); + + if (music.info.channels == 2) music.format = AL_FORMAT_STEREO16; + else music.format = AL_FORMAT_MONO16; + + music.bufferSize = 4096*8; + music.loop = true; // We loop by default + + // Create an audio source + alGenSources(1, &music.source); // Generate pointer to audio source + + alSourcef(music.source, AL_PITCH, 1); + alSourcef(music.source, AL_GAIN, 1); + alSource3f(music.source, AL_POSITION, 0, 0, 0); + alSource3f(music.source, AL_VELOCITY, 0, 0, 0); + alSourcei(music.source, AL_LOOPING, AL_TRUE); // We loop by default + + // Convert loaded data to OpenAL buffers + alGenBuffers(2, music.buffers); + /* + if (!MusicStream(music, music.buffers[0])) exit(1); + if (!MusicStream(music, music.buffers[1])) exit(1); + + alSourceQueueBuffers(music.source, 2, music.buffers); + + PlayMusic(music); + */ + music.totalSamplesLeft = stb_vorbis_stream_length_in_samples(music.stream) * music.info.channels; + + currentMusic = &music; + } + + return music; +} + +void UnloadMusic(Music music) +{ + StopMusic(music); + + alDeleteSources(1, &music.source); + alDeleteBuffers(2, music.buffers); + + stb_vorbis_close(music.stream); +} + +void PlayMusic(Music music) +{ + //if (MusicIsPlaying(music)) return true; + + if (!MusicStream(music, music.buffers[0])) TraceLog(WARNING, "MusicStream returned 0"); + if (!MusicStream(music, music.buffers[1])) TraceLog(WARNING, "MusicStream returned 0"); + + alSourceQueueBuffers(music.source, 2, music.buffers); + alSourcePlay(music.source); + + TraceLog(INFO, "Playing music"); +} + +extern void PlayCurrentMusic() +{ + if (!MusicStream(*currentMusic, currentMusic->buffers[0])) TraceLog(WARNING, "MusicStream returned 0"); + if (!MusicStream(*currentMusic, currentMusic->buffers[1])) TraceLog(WARNING, "MusicStream returned 0"); + + alSourceQueueBuffers(currentMusic->source, 2, currentMusic->buffers); + alSourcePlay(currentMusic->source); +} + +// Stop reproducing music +void StopMusic(Music music) +{ + alSourceStop(music.source); + + musicIsPlaying = false; +} + +static bool MusicStream(Music music, ALuint buffer) +{ + //Uncomment this to avoid VLAs + //#define BUFFER_SIZE 4096*32 + #ifndef BUFFER_SIZE//VLAs ftw + #define BUFFER_SIZE (music.bufferSize) + #endif + ALshort pcm[BUFFER_SIZE]; + + int size = 0; + int result = 0; + + while (size < BUFFER_SIZE) + { + result = stb_vorbis_get_samples_short_interleaved(music.stream, music.info.channels, pcm+size, BUFFER_SIZE-size); + + if (result > 0) size += (result*music.info.channels); + else break; + } + + if (size == 0) return false; + + alBufferData(buffer, music.format, pcm, size*sizeof(ALshort), music.info.sample_rate); + + music.totalSamplesLeft -= size; + + #undef BUFFER_SIZE + + return true; +} +/* +extern bool MusicStreamUpdate() +{ + ALint processed = 0; + + alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed); + + while (processed--) + { + ALuint buffer = 0; + + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + + if (!MusicStream(*currentMusic, buffer)) + { + bool shouldExit = true; + + if (currentMusic->loop) + { + stb_vorbis_seek_start(currentMusic->stream); + currentMusic->totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic->stream) * currentMusic->info.channels; + + shouldExit = !MusicStream(*currentMusic, buffer); + } + + if (shouldExit) return false; + } + + alSourceQueueBuffers(currentMusic->source, 1, &buffer); + } + + return true; +} +*/ +extern bool MusicStreamUpdate() +{ + int processed; + bool active = true; + + alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed); + + printf("Data processed: %i\n", processed); + + while (processed--) + { + ALuint buffer = 0; + + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + + active = MusicStream(*currentMusic, buffer); + + alSourceQueueBuffers(currentMusic->source, 1, &buffer); + } + + return active; +} +void MusicStreamEmpty() +{ + int queued; + + alGetSourcei(currentMusic->source, AL_BUFFERS_QUEUED, &queued); + + while(queued--) + { + ALuint buffer; + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + } +} \ No newline at end of file diff --git a/src/core.c b/src/core.c index 0d18f57a..79866a2f 100644 --- a/src/core.c +++ b/src/core.c @@ -92,6 +92,9 @@ static Color background = { 0, 0, 0, 0 }; // Screen background color extern void LoadDefaultFont(); // [Module: text] Loads default font on InitWindow() extern void UnloadDefaultFont(); // [Module: text] Unloads default font from GPU memory +extern bool MusicStreamUpdate(); // [Module: audio] Updates buffers for music streamming +extern void PlayCurrentMusic(); // [Module: audio] Plays current music stream + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -117,7 +120,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons { glfwSetErrorCallback(ErrorCallback); - if (!glfwInit()) exit(1); + if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); //glfwDefaultWindowHints() // Set default windows hints @@ -140,7 +143,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons if (!window) { glfwTerminate(); - exit(1); + TraceLog(ERROR, "Failed to initialize Window"); } glfwSetWindowSizeCallback(window, WindowSizeCallback); @@ -154,7 +157,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons // Framerate can be setup using SetTargetFPS() //------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlglInit(); // Init rlgl #endif //------------------------------------------------------ @@ -183,7 +186,7 @@ void CloseWindow() UnloadDefaultFont(); //------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlglClose(); // De-init rlgl #endif //------------------------------------------------------ @@ -230,13 +233,21 @@ void ToggleFullscreen() // TODO: WARNING! All loaded resources are lost, we loose Context! // NOTE: Window aspect ratio is always windowWidth / windowHeight - if (fullscreen) window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); // Fullscreen mode + if (fullscreen) + { + // TODO: Get desktop window size and adapt aspect-ratio (?) + //const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + //windowWidth = mode->width; + //windowHeight = mode->height; + + window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); // Fullscreen mode + } else window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL); - + if (!window) { glfwTerminate(); - exit(1); + TraceLog(ERROR, "Failed to initialize Window when switching fullscreen mode"); } glfwMakeContextCurrent(window); @@ -285,7 +296,7 @@ void EndDrawing() if (customCursor && cursorOnScreen) DrawTexture(cursor, GetMouseX(), GetMouseY(), WHITE); //------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlglDraw(); // Draw Buffers #endif //------------------------------------------------------ @@ -293,6 +304,10 @@ void EndDrawing() glfwSwapBuffers(window); // Swap back and front buffers glfwPollEvents(); // Register keyboard/mouse events + //MusicStreamUpdate(); + //if (!MusicIsPlaying()) + //PlayCurrentMusic(); + currentTime = glfwGetTime(); drawTime = currentTime - previousTime; previousTime = currentTime; @@ -315,7 +330,7 @@ void EndDrawing() void Begin3dMode(Camera camera) { //------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlglDraw(); // Draw Buffers #endif //------------------------------------------------------ @@ -344,7 +359,7 @@ void Begin3dMode(Camera camera) void End3dMode() { //------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlglDraw(); // Draw Buffers #endif //------------------------------------------------------ @@ -363,7 +378,7 @@ void SetTargetFPS(int fps) { targetTime = 1 / (float)fps; - printf("TargetTime per Frame: %f seconds\n", (float)targetTime); + TraceLog(INFO, "Target time per frame: %02.03f milliseconds", (float)targetTime*1000); } // Returns current FPS @@ -431,18 +446,18 @@ Color Fade(Color color, float alpha) // Detect if a key has been pressed once bool IsKeyPressed(int key) { - bool ret = false; + bool pressed = false; currentKeyState[key] = IsKeyDown(key); if (currentKeyState[key] != previousKeyState[key]) { - if (currentKeyState[key]) ret = true; + if (currentKeyState[key]) pressed = true; previousKeyState[key] = currentKeyState[key]; } - else ret = false; + else pressed = false; - return ret; + return pressed; } // Detect if a key is being pressed (key held down) @@ -455,18 +470,18 @@ bool IsKeyDown(int key) // Detect if a key has been released once bool IsKeyReleased(int key) { - bool ret = false; + bool released = false; currentKeyState[key] = IsKeyUp(key); if (currentKeyState[key] != previousKeyState[key]) { - if (currentKeyState[key]) ret = true; + if (currentKeyState[key]) released = true; previousKeyState[key] = currentKeyState[key]; } - else ret = false; + else released = false; - return ret; + return released; } // Detect if a key is NOT being pressed (key not held down) @@ -479,18 +494,18 @@ bool IsKeyUp(int key) // Detect if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { - bool ret = false; + bool pressed = false; currentMouseState[button] = IsMouseButtonDown(button); if (currentMouseState[button] != previousMouseState[button]) { - if (currentMouseState[button]) ret = true; + if (currentMouseState[button]) pressed = true; previousMouseState[button] = currentMouseState[button]; } - else ret = false; + else pressed = false; - return ret; + return pressed; } // Detect if a mouse button is being pressed @@ -503,18 +518,18 @@ bool IsMouseButtonDown(int button) // Detect if a mouse button has been released once bool IsMouseButtonReleased(int button) { - bool ret = false; + bool released = false; currentMouseState[button] = IsMouseButtonUp(button); if (currentMouseState[button] != previousMouseState[button]) { - if (currentMouseState[button]) ret = true; + if (currentMouseState[button]) released = true; previousMouseState[button] = currentMouseState[button]; } - else ret = false; + else released = false; - return ret; + return released; } // Detect if a mouse button is NOT being pressed @@ -603,18 +618,18 @@ Vector2 GetGamepadMovement(int gamepad) // Detect if a gamepad button is being pressed bool IsGamepadButtonPressed(int gamepad, int button) { - bool ret = false; + bool pressed = false; currentGamepadState[button] = IsGamepadButtonDown(gamepad, button); if (currentGamepadState[button] != previousGamepadState[button]) { - if (currentGamepadState[button]) ret = true; + if (currentGamepadState[button]) pressed = true; previousGamepadState[button] = currentGamepadState[button]; } - else ret = false; + else pressed = false; - return ret; + return pressed; } bool IsGamepadButtonDown(int gamepad, int button) @@ -634,18 +649,18 @@ bool IsGamepadButtonDown(int gamepad, int button) // Detect if a gamepad button is NOT being pressed bool IsGamepadButtonReleased(int gamepad, int button) { - bool ret = false; + bool released = false; currentGamepadState[button] = IsGamepadButtonUp(gamepad, button); if (currentGamepadState[button] != previousGamepadState[button]) { - if (currentGamepadState[button]) ret = true; + if (currentGamepadState[button]) released = true; previousGamepadState[button] = currentGamepadState[button]; } - else ret = false; + else released = false; - return ret; + return released; } bool IsGamepadButtonUp(int gamepad, int button) @@ -669,8 +684,7 @@ bool IsGamepadButtonUp(int gamepad, int button) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { - printf(description); - //fprintf(stderr, description); + TraceLog(WARNING, "GLFW3 Error: %s", description); } // GLFW3 Srolling Callback, runs on mouse wheel diff --git a/src/models.c b/src/models.c index fb8f114d..84ef43fa 100644 --- a/src/models.c +++ b/src/models.c @@ -568,53 +568,48 @@ void DrawGizmo(Vector3 position) rlPopMatrix(); } -void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits) +void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits) { + static float rotation = 0; // NOTE: RGB = XYZ rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); + rlRotatef(rotation, 0, 1, 0); rlScalef(scale, scale, scale); - - if(rotation.x) rlRotatef(rotation.x, 1, 0, 0); - if(rotation.y) rlRotatef(rotation.y, 0, 1, 0); - if(rotation.z) rlRotatef(rotation.z, 0, 0, 1); - + rlBegin(RL_LINES); // X Axis - rlColor4ub(200, 0, 0, 255); // RED - rlVertex3f(position.x, position.y, position.z); - rlVertex3f(position.x + 1, position.y, position.z); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x, position.y, position.z); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1, position.y, position.z); // ArrowX - rlVertex3f(position.x + 1.1, position.y, position.z); - rlVertex3f(position.x + .9, position.y, position.z + .1); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z + .1); - rlVertex3f(position.x + 1.1, position.y, position.z); - rlVertex3f(position.x + .9, position.y, position.z - .1); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); + rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z - .1); // Y Axis - rlColor4ub(0, 200, 0, 255); // GREEN - rlVertex3f(position.x, position.y, position.z); - rlVertex3f(position.x, position.y + 1, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1, position.z); // ArrowY - rlVertex3f(position.x, position.y + 1.1, position.z); - rlVertex3f(position.x + .1, position.y + .9, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + .1, position.y + .9, position.z); - rlVertex3f(position.x, position.y + 1.1, position.z); - rlVertex3f(position.x - .1, position.y + .9, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x - .1, position.y + .9, position.z); // Z Axis - rlColor4ub(0, 0, 200, 255); // BLUE - rlVertex3f(position.x, position.y, position.z); - rlVertex3f(position.x, position.y, position.z - 1); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1); // ArrowZ - rlVertex3f(position.x, position.y, position.z - 1.1); - rlVertex3f(position.x + .1, position.y, position.z - .9); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + .1, position.y, position.z - .9); - rlVertex3f(position.x, position.y, position.z - 1.1); - rlVertex3f(position.x - .1, position.y, position.z - .9); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9); // Extra if(orbits) @@ -622,31 +617,30 @@ void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits) int n = 3; // X Axis - rlColor4ub(200, 0, 0, 255); // RED - for (int i=0; i < 360; i++) + for (int i=0; i < 360; i += 6) { - rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n); - rlVertex3f(0,position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n); + rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n); + rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n); } // Y Axis - rlColor4ub(0, 200, 0, 255); // GREEN - for (int i=0; i < 360; i++) + for (int i=0; i < 360; i += 6) { - rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n); - rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, 0, position.y + cos(DEG2RAD*(i+1)) * scale/n); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n); + rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n); } // Z Axis - rlColor4ub(0, 0, 200, 255); // BLUE - for (int i=0; i < 360; i++) + for (int i=0; i < 360; i += 6) { - rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0); - rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n, 0); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0); + rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0); } } rlEnd(); rlPopMatrix(); + + rotation += 0.1f; } // Load a 3d model (.OBJ) @@ -696,7 +690,6 @@ Model LoadModel(const char *fileName) { fscanf(objFile, "%i", &numTexCoords); } - else printf("Ouch! Something was wrong..."); fgets(comments, 200, objFile); } @@ -715,7 +708,6 @@ Model LoadModel(const char *fileName) { fscanf(objFile, "%i", &numNormals); } - else printf("Ouch! Something was wrong..."); fgets(comments, 200, objFile); } @@ -734,7 +726,6 @@ Model LoadModel(const char *fileName) { fscanf(objFile, "%i", &numVertex); } - else printf("Ouch! Something was wrong..."); fgets(comments, 200, objFile); } @@ -754,7 +745,6 @@ Model LoadModel(const char *fileName) { fscanf(objFile, "%i", &numTriangles); } - else printf("Ouch! Something was wrong..."); fgets(comments, 200, objFile); @@ -1024,7 +1014,7 @@ void UnloadModel(Model model) free(model.data.normals); #endif -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) rlDeleteVertexArrays(model.vaoId); #endif } @@ -1032,7 +1022,7 @@ void UnloadModel(Model model) // Draw a model void DrawModel(Model model, Vector3 position, float scale, Color color) { - rlglDrawModel(model, false); + rlglDrawModel(model, position, scale, false); } // Draw a textured model @@ -1048,7 +1038,7 @@ void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, // Draw a model wires void DrawModelWires(Model model, Vector3 position, float scale, Color color) { - rlglDrawModel(model, true); + rlglDrawModel(model, position, scale, true); } // Draw a billboard diff --git a/src/raylib.h b/src/raylib.h index 450d2e6f..237f635d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -54,6 +54,8 @@ #ifndef RAYLIB_H #define RAYLIB_H +#include "stb_vorbis.h" + //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- @@ -153,6 +155,19 @@ // Boolean type typedef enum { false, true } bool; +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; + +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; + // Color type, RGBA (32bit) typedef struct Color { unsigned char r; @@ -185,45 +200,49 @@ typedef struct Texture2D { int height; } Texture2D; -// SpriteFont one Character (Glyph) data, defined in text module +// Camera type, defines a camera position/orientation in 3d space +typedef struct Camera { + Vector3 position; + Vector3 target; + Vector3 up; +} Camera; + typedef struct Character Character; -// SpriteFont type, includes texture and charSet array data +// SpriteFont type typedef struct SpriteFont { Texture2D texture; int numChars; Character *charSet; } SpriteFont; -// Vector2 type -typedef struct Vector2 { - float x; - float y; -} Vector2; - -// Vector3 type -typedef struct Vector3 { - float x; - float y; - float z; -} Vector3; - -// Camera type, defines a camera position/orientation in 3d space -typedef struct Camera { - Vector3 position; - Vector3 target; - Vector3 up; -} Camera; +// 3d Model type +// NOTE: If using OpenGL 1.1 loaded in CPU; if OpenGL 3.3+ loaded in GPU +typedef struct Model Model; // Defined in module: rlgl -// Basic 3d Model type -typedef struct Model Model; - -// Basic Sound source and buffer +// Sound source type typedef struct Sound { unsigned int source; unsigned int buffer; } Sound; +typedef struct OggStream OggStream; + +// Music type (streamming) +typedef struct Music { + stb_vorbis *stream; + stb_vorbis_info info; + + unsigned int source; + unsigned int buffers[2]; + + int format; + + int bufferSize; + int totalSamplesLeft; + bool loop; +} Music; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -365,7 +384,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) void DrawGizmo(Vector3 position); // Draw simple gizmo -void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits); // Draw gizmo with extended parameters +void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits); // Draw gizmo with extended parameters //DrawTorus(), DrawTeapot() are useless... //------------------------------------------------------------------------------------ @@ -389,13 +408,18 @@ void CloseAudioDevice(); // Close the aud Sound LoadSound(char *fileName); // Load sound to memory Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) void UnloadSound(Sound sound); // Unload sound +Music LoadMusic(char *fileName); +void UnloadMusic(Music music); void PlaySound(Sound sound); // Play a sound void PauseSound(Sound sound); // Pause a sound void StopSound(Sound sound); // Stop playing a sound -bool IsPlaying(Sound sound); // Check if a sound is currently playing +bool SoundIsPlaying(Sound sound); // Check if a sound is currently playing void SetVolume(Sound sound, float volume); // Set volume for a sound (1.0 is base level) void SetPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) +void PlayMusic(Music music); +void StopMusic(Music music); +bool MusicIsPlaying(); #ifdef __cplusplus } diff --git a/src/rlgl.c b/src/rlgl.c index e25e72be..8a758a22 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -31,7 +31,7 @@ #include // Standard input / output lib #include // Declares malloc() and free() for memory management, rand() -#include "raymath.h" // Required for data type Matrix and Matrix functions +// TODO: Security check in case multiple USE_OPENGL_* defined #ifdef USE_OPENGL_11 #include // Extensions loading lib @@ -51,7 +51,7 @@ //---------------------------------------------------------------------------------- #define MATRIX_STACK_SIZE 16 // Matrix stack max size #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes -#define TEMP_VERTEX_BUFFER_SIZE 1024 // Temporal Vertex Buffer (required for vertex-transformations) +#define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -226,9 +226,7 @@ void rlPushMatrix() { if (stackCounter == MATRIX_STACK_SIZE - 1) { - printf("ERROR: Stack Buffer Overflow! (MAX 16 MATRIX)"); - - exit(1); + TraceLog(ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); } stack[stackCounter] = *currentMatrix; @@ -667,17 +665,22 @@ void rlglInit() if (error != GLEW_OK) { - fprintf(stderr, "Failed to initialize GLEW - Error: %s\n", glewGetErrorString(error)); - exit(1); + TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error)); } - if (glewIsSupported("GL_VERSION_3_3")) printf("OpenGL 3.3 initialized\n"); + if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 initialized\n"); + + // Print OpenGL and GLSL version + TraceLog(INFO, "Vendor: %s", glGetString(GL_VENDOR)); + TraceLog(INFO, "Renderer: %s", glGetString(GL_RENDERER)); + TraceLog(INFO, "Version: %s", glGetString(GL_VERSION)); + TraceLog(INFO, "GLSL: %s\n", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION + /* // TODO: GLEW is a big library that loads ALL extensions, maybe using glad we can only load required ones... if (!gladLoadGL()) { - fprintf(stderr, printf("Failed to initialize glad.\n"); - exit(1); + TraceLog("ERROR: Failed to initialize glad\n"); } */ // Set default draw mode @@ -707,13 +710,7 @@ void rlglInit() // Get handles to GLSL uniform vars locations (fragment-shader) textureLoc = glGetUniformLocation(shaderProgram, "texture0"); - printf("Default shaders loaded\n\n"); - - // Print OpenGL and GLSL version - printf("Vendor: %s\n", glGetString(GL_VENDOR)); - printf("Renderer: %s\n", glGetString(GL_RENDERER)); - printf("Version: %s\n", glGetString(GL_VERSION)); - printf("GLSL: %s\n\n", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION + TraceLog(INFO, "Default shader loaded"); InitializeBuffers(); // Init vertex arrays InitializeVAOs(); // Init VBO and VAO @@ -726,7 +723,10 @@ void rlglInit() // Create default white texture for plain colors (required by shader) unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglLoadTexture(1, 1, pixels); + whiteTexture = rlglLoadTexture(1, 1, pixels); + + if (whiteTexture != 0) TraceLog(INFO, "Base white texture successfully created, id: %i", whiteTexture); + else TraceLog(WARNING, "Base white texture could not be created"); // Init draw calls tracking system draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); @@ -836,16 +836,14 @@ void rlglDraw() glBindVertexArray(vaoQuads); } - //printf("\nRequired Draws: %i\n", drawsCounter); + //TraceLog(INFO, "Draws required per frame: %i", drawsCounter); for (int i = 0; i < drawsCounter; i++) { numQuads = draws[i].vCount/4; numIndicesToProcess = numQuads*6; // Get number of Quads * 6 index by Quad - //printf("Quads to render: %i - ", numQuads); - //printf("Vertex Count: %i - ", draws[i].vCount); - //printf("Binding texture: %i\n", draws[i].texId); + //TraceLog(INFO, "Quads to render: %i - Vertex Count: %i", numQuads, draws[i].vCount); glBindTexture(GL_TEXTURE_2D, draws[i].texId); @@ -882,7 +880,10 @@ void rlglDraw() #endif } -void rlglDrawModel(Model model, bool wires) +#endif // End for OpenGL 3.3+ and ES2 only functions + +// Draw a 3d model +void rlglDrawModel(Model model, Vector3 position, float scale, bool wires) { if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -892,19 +893,19 @@ void rlglDrawModel(Model model, bool wires) glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array - glVertexPointer(3, GL_FLOAT, 0, model.vertices); // Pointer to vertex coords array - glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords); // Pointer to texture coords array - glNormalPointer(GL_FLOAT, 0, model.normals); // Pointer to normals array + glVertexPointer(3, GL_FLOAT, 0, model.data.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, model.data.texcoords); // Pointer to texture coords array + glNormalPointer(GL_FLOAT, 0, model.data.normals); // Pointer to normals array //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED) rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); - //glRotatef(rotation * GetFrameTime(), 0, 1, 0); + //rlRotatef(rotation * GetFrameTime(), 0, 1, 0); rlScalef(scale, scale, scale); - rlColor4ub(color.r, color.g, color.b, color.a); + rlColor4ub(1.0f, 1.0f, 1.0f, 1.0f); - glDrawArrays(GL_TRIANGLES, 0, model.numVertices); + glDrawArrays(GL_TRIANGLES, 0, model.data.numVertices); rlPopMatrix(); glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array @@ -912,7 +913,7 @@ void rlglDrawModel(Model model, bool wires) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array #endif -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) glUseProgram(shaderProgram); // Use our shader Matrix modelview2 = MatrixMultiply(model.transform, modelview); @@ -934,8 +935,6 @@ void rlglDrawModel(Model model, bool wires) if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } -#endif - // Initialize Graphics Device (OpenGL stuff) void rlglInitGraphicsDevice(int fbWidth, int fbHeight) { @@ -968,7 +967,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) - // TODO: Review all shapes/models are drawn CCW and enable backface culling + // NOTE: All shapes/models triangles are drawn CCW glEnable(GL_CULL_FACE); // Enable backface culling (Disabled by default) //glCullFace(GL_BACK); // Cull the Back face (default) @@ -978,11 +977,13 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight) glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) (Deprecated on OpenGL 3.3+) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif + + TraceLog(INFO, "OpenGL graphics device initialized"); } // Convert image data to OpenGL texture (returns OpenGL valid Id) // NOTE: Image is not unloaded, it should be done manually... -unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels) +unsigned int rlglLoadTexture(int width, int height, unsigned char *data) { glBindTexture(GL_TEXTURE_2D,0); // Free any old binding @@ -998,7 +999,7 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels) 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 -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) // Trilinear filtering //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) @@ -1008,20 +1009,84 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels) // NOTE: Not using mipmappings (texture for 2D drawing) // At this point we have the image converted to texture and uploaded to GPU - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // At this point we have the image converted to texture and uploaded to GPU // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); - printf("New texture created, id: %i (%i x %i)\n", id, width, height); + TraceLog(INFO, "New texture created, id: %i (%i x %i)", id, width, height); return id; } -#ifdef USE_OPENGL_33 -unsigned int rlglLoadModel(VertexData data) + +#ifdef USE_OPENGL_33 + +#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 + +// Convert image data to OpenGL texture (returns OpenGL valid Id) +// NOTE: Expected compressed data from DDS file +unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format) +{ + // Create one OpenGL texture + GLuint id; + int compFormat = 0; + + TraceLog(DEBUG, "Compressed texture width: %i", width); + TraceLog(DEBUG, "Compressed texture height: %i", height); + TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount); + TraceLog(DEBUG, "Compressed texture format: 0x%x", format); + + switch(format) + { + case FOURCC_DXT1: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case FOURCC_DXT3: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case FOURCC_DXT5: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + default: compFormat = -1; break; + } + + if (compFormat == -1) + { + TraceLog(WARNING, "Texture compressed format not recognized"); + id = 0; + } + else + { + glGenTextures(1, &id); + + // Bind the texture + glBindTexture(GL_TEXTURE_2D, id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + unsigned int blockSize = (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; + unsigned int offset = 0; + + // Load the mipmaps + for (int level = 0; level < mipmapCount && (width || height); level++) + { + unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize; + + glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); + + offset += size; + width /= 2; + height /= 2; + + // Security check for NPOT textures + if (width < 1) width = 1; + if (height < 1) height = 1; + } + } + + return id; +} + +// Load vertex data into a VAO +unsigned int rlglLoadModel(VertexData mesh) { GLuint vaoModel; // Vertex Array Objects (VAO) GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) @@ -1035,17 +1100,17 @@ unsigned int rlglLoadModel(VertexData data) // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.vertices, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(vertexLoc); glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*data.numVertices, data.texcoords, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.numVertices, mesh.texcoords, GL_STATIC_DRAW); glEnableVertexAttribArray(texcoordLoc); glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.normals, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.normals, GL_STATIC_DRAW); //glEnableVertexAttribArray(normalLoc); //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0); @@ -1077,7 +1142,7 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) void PrintProjectionMatrix() { @@ -1154,7 +1219,7 @@ static GLuint LoadDefaultShaders() glDeleteShader(vertexShader); glDeleteShader(fragmentShader); - return(program); + return program; } @@ -1191,14 +1256,14 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName) glDeleteShader(vertexShader); glDeleteShader(fragmentShader); - return(program); + return program; } // Read shader text file static char *TextFileRead(char *fn) { FILE *fp; - char *content = NULL; + char *text = NULL; int count=0; @@ -1214,15 +1279,15 @@ static char *TextFileRead(char *fn) if (count > 0) { - content = (char *)malloc(sizeof(char) * (count+1)); - count = fread(content, sizeof(char), count, fp); - content[count] = '\0'; + text = (char *)malloc(sizeof(char) * (count+1)); + count = fread(text, sizeof(char), count, fp); + text[count] = '\0'; } fclose(fp); } } - return content; + return text; } // Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) @@ -1377,10 +1442,10 @@ static void InitializeVAOs() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBufferB[3]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); - printf("Using VBO double buffering\n"); + TraceLog(INFO, "Using VBO double buffering"); #endif - printf("Vertex buffers initialized (lines, triangles, quads)\n\n"); + TraceLog(INFO, "Vertex buffers successfully initialized (lines, triangles, quads)\n"); // Unbind the current VAO glBindVertexArray(0); @@ -1475,4 +1540,33 @@ static void UpdateBuffers() glBindVertexArray(0); } +#endif + +#ifdef RLGL_STANDALONE + +typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; + +// Output a trace log message +// NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning +void TraceLog(int msgType, const char *text, ...) +{ + va_list args; + va_start(args, text); + + switch(msgType) + { + case 0: fprintf(stdout, "INFO: "); break; + case 1: fprintf(stdout, "ERROR: "); break; + case 2: fprintf(stdout, "WARNING: "); break; + case 3: fprintf(logstream, "DEBUG: "); break; + default: break; + } + + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); + + va_end(args); + + if (msgType == 1) exit(1); +} #endif \ No newline at end of file diff --git a/src/rlgl.h b/src/rlgl.h index 174310dd..0cd53661 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -33,17 +33,20 @@ #ifndef RLGL_STANDALONE #include "raylib.h" // Required for typedef: Model + #include "utils.h" // Required for function TraceLog() #endif +#include "raymath.h" // Required for data type Matrix and Matrix functions + // Select desired OpenGL version -#define USE_OPENGL_11 -//#define USE_OPENGL_33 +//#define USE_OPENGL_11 +#define USE_OPENGL_33 //#define USE_OPENGL_ES2 //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_LINES_BATCH 1024 +#define MAX_LINES_BATCH 8192 // 1024 #define MAX_TRIANGLES_BATCH 2048 #define MAX_QUADS_BATCH 8192 @@ -57,7 +60,7 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode; typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; #ifdef RLGL_STANDALONE - typedef struct Model Model; +typedef struct Model Model; #endif typedef struct { @@ -125,19 +128,20 @@ void rlClearScreenBuffers(); // Clear used screen buffers (color //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) void rlglInit(); // Initialize rlgl (shaders, VAO, VBO...) void rlglClose(); // De-init rlgl void rlglDraw(); // Draw VAOs unsigned int rlglLoadModel(VertexData data); +unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); #endif -void rlglDrawModel(Model model, bool wires); // Draw model +void rlglDrawModel(Model model, Vector3 position, float scale, bool wires); // Draw model void rlglInitGraphicsDevice(int fbWidth, int fbHeight); // Initialize Graphics Device (OpenGL stuff) unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels); // Load in GPU OpenGL texture byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -#ifdef USE_OPENGL_33 +#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2) void PrintProjectionMatrix(); // DEBUG: Print projection matrix void PrintModelviewMatrix(); // DEBUG: Print modelview matrix #endif diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c index cf7ad1d4..a88b2a77 100644 --- a/src/stb_vorbis.c +++ b/src/stb_vorbis.c @@ -11,6 +11,7 @@ // Get the latest version and other information at: // http://nothings.org/stb_vorbis/ + // Todo: // // - seeking (note you can seek yourself using the pushdata API) @@ -25,6 +26,9 @@ // // All of these limitations may be removed in future versions. + +#include "stb_vorbis.h" + #ifndef STB_VORBIS_HEADER_ONLY // global configuration settings (e.g. set these in the project/makefile), diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h index 98c1209a..2d7b61e5 100644 --- a/src/stb_vorbis.h +++ b/src/stb_vorbis.h @@ -11,6 +11,7 @@ // Get the latest version and other information at: // http://nothings.org/stb_vorbis/ + // Todo: // // - seeking (note you can seek yourself using the pushdata API) @@ -25,12 +26,6 @@ // // All of these limitations may be removed in future versions. - -////////////////////////////////////////////////////////////////////////////// -// -// HEADER BEGINS HERE -// - #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H #define STB_VORBIS_INCLUDE_STB_VORBIS_H @@ -349,4 +344,4 @@ enum STBVorbisError } #endif -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H \ No newline at end of file diff --git a/src/text.c b/src/text.c index 2d75a6d9..4cc36baf 100644 --- a/src/text.c +++ b/src/text.c @@ -50,13 +50,22 @@ typedef unsigned char byte; // SpriteFont one Character (Glyph) data -struct Character { +typedef struct Character { int value; //char value = ' '; (int)value = 32; int x; int y; int w; int h; +} Character; + +// SpriteFont type, includes texture and charSet array data +/* +struct SpriteFont { + Texture2D texture; + int numChars; + Character *charSet; }; +*/ //---------------------------------------------------------------------------------- // Global variables @@ -177,6 +186,8 @@ extern void LoadDefaultFont() } else currentPosX = testPosX; } + + TraceLog(INFO, "Default font loaded successfully"); } extern void UnloadDefaultFont() @@ -232,8 +243,8 @@ SpriteFont LoadSpriteFont(const char* fileName) // spriteFont.charSet data is filled inside the function and memory is allocated! int numChars = ParseImageData(imgDataPixel, imgWidth, imgHeight, &spriteFont.charSet); - fprintf(stderr, "SpriteFont data parsed correctly!\n"); - fprintf(stderr, "SpriteFont num chars: %i\n", numChars); + TraceLog(INFO, "[%s] SpriteFont data parsed correctly", fileName); + TraceLog(INFO, "[%s] SpriteFont num chars detected: %i", numChars); spriteFont.numChars = numChars; @@ -257,7 +268,7 @@ SpriteFont LoadSpriteFont(const char* fileName) } } - fprintf(stderr, "SpriteFont texture converted to POT: %i %i\n", potWidth, potHeight); + TraceLog(WARNING, "SpriteFont texture converted to POT: %ix%i", potWidth, potHeight); } free(imgDataPixel); @@ -347,7 +358,7 @@ const char *FormatText(const char *text, ...) va_list args; va_start(args, text); - vsprintf(buffer, text, args); // NOTE: We use vsprintf() defined in + vsprintf(buffer, text, args); va_end(args); return buffer; @@ -547,7 +558,7 @@ static SpriteFont LoadRBMF(const char *fileName) fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile); - //printf("rBMF info: %i %i %i %i\n", rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight); + TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight); spriteFont.numChars = (int)rbmfHeader.numChars; @@ -564,8 +575,6 @@ static SpriteFont LoadRBMF(const char *fileName) for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile); - printf("Just read image data and width data... Starting image reconstruction..."); - // Re-construct image from rbmfFileData //----------------------------------------- image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); @@ -585,13 +594,13 @@ static SpriteFont LoadRBMF(const char *fileName) counter++; } - printf("Image reconstructed correctly... now converting it to texture..."); + TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName); spriteFont.texture = CreateTexture(image); UnloadImage(image); // Unload image data - printf("Starting charSet reconstruction...\n"); + TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName); // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character)); // Allocate space for our character data @@ -620,11 +629,9 @@ static SpriteFont LoadRBMF(const char *fileName) spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor); } else currentPosX = testPosX; - - //printf("Char %i data: %i %i %i %i\n", spriteFont.charSet[i].value, spriteFont.charSet[i].x, spriteFont.charSet[i].y, spriteFont.charSet[i].w, spriteFont.charSet[i].h); } - printf("CharSet reconstructed correctly... Data should be ready...\n"); + TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName); fclose(rbmfFile); @@ -634,6 +641,7 @@ static SpriteFont LoadRBMF(const char *fileName) return spriteFont; } +// Get the extension for a filename static const char *GetExtension(const char *fileName) { const char *dot = strrchr(fileName, '.'); diff --git a/src/textures.c b/src/textures.c index 75300eac..7d4a0fb4 100644 --- a/src/textures.c +++ b/src/textures.c @@ -29,8 +29,8 @@ #include "raylib.h" #include // Declares malloc() and free() for memory management +#include // Required for strcmp(), strrchr(), strncmp() #include "stb_image.h" // Used to read image data (multiple formats support) - #include "utils.h" // rRES data decompression utility function #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 @@ -45,6 +45,14 @@ //---------------------------------------------------------------------------------- typedef unsigned char byte; +typedef struct { + unsigned char *data; + int width; + int height; + int mipmaps; + int format; +} ImageDDS; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -58,7 +66,8 @@ typedef unsigned char byte; //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -// No private (static) functions in this module (.c file) +static const char *GetExtension(const char *fileName); +static ImageDDS LoadDDS(const char *fileName); //---------------------------------------------------------------------------------- // Module Functions Definition @@ -69,32 +78,44 @@ Image LoadImage(const char *fileName) { Image image; - int imgWidth; - int imgHeight; - int imgBpp; - - // 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); - - // 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++; + if ((strcmp(GetExtension(fileName),"png") == 0) || + (strcmp(GetExtension(fileName),"bmp") == 0) || + (strcmp(GetExtension(fileName),"tga") == 0) || + (strcmp(GetExtension(fileName),"jpg") == 0) || + (strcmp(GetExtension(fileName),"gif") == 0) || + (strcmp(GetExtension(fileName),"psd") == 0) || + (strcmp(GetExtension(fileName),"pic") == 0)) + { + int imgWidth; + int imgHeight; + int imgBpp; + + // 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); + + // 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", fileName); } - - stbi_image_free(imgData); - - image.width = imgWidth; - image.height = imgHeight; + 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. @@ -119,103 +140,104 @@ Image LoadImageFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (!rresFile) printf("Error opening raylib Resource file\n"); - - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) - { - printf("This is not a valid raylib Resource file!\n"); - exit(1); - } - - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) + if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName); + else { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); + // Read rres file (basic file check - id) + fread(&id[0], sizeof(char), 1, rresFile); + fread(&id[1], sizeof(char), 1, rresFile); + fread(&id[2], sizeof(char), 1, rresFile); + fread(&id[3], sizeof(char), 1, rresFile); + fread(&version, sizeof(char), 1, rresFile); + fread(&useless, sizeof(char), 1, rresFile); - if (infoHeader.id == resId) + if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) + { + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); + } + else { - found = true; + // Read number of resources embedded + fread(&numRes, sizeof(short), 1, rresFile); - // Check data is of valid IMAGE type - if (infoHeader.type == 0) // IMAGE data type + for (int i = 0; i < numRes; i++) { - // TODO: Check data compression type - - // NOTE: We suppose compression type 2 (DEFLATE - default) - short imgWidth, imgHeight; - char colorFormat, mipmaps; - - fread(&imgWidth, sizeof(short), 1, rresFile); // Image width - fread(&imgHeight, sizeof(short), 1, rresFile); // Image height - fread(&colorFormat, 1, 1, rresFile); // Image data color format (default: RGBA 32 bit) - fread(&mipmaps, 1, 1, rresFile); // Mipmap images included (default: 0) - - printf("Image width: %i\n", (int)imgWidth); - printf("Image height: %i\n", (int)imgHeight); + fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - image.width = (int)imgWidth; - image.height = (int)imgHeight; + if (infoHeader.id == resId) + { + found = true; + + // Check data is of valid IMAGE type + if (infoHeader.type == 0) // IMAGE data type + { + // TODO: Check data compression type + + // NOTE: We suppose compression type 2 (DEFLATE - default) + short imgWidth, imgHeight; + char colorFormat, mipmaps; + + fread(&imgWidth, sizeof(short), 1, rresFile); // Image width + fread(&imgHeight, sizeof(short), 1, rresFile); // Image height + fread(&colorFormat, 1, 1, rresFile); // Image data color format (default: RGBA 32 bit) + fread(&mipmaps, 1, 1, rresFile); // Mipmap images included (default: 0) - unsigned char *data = malloc(infoHeader.size); + image.width = (int)imgWidth; + image.height = (int)imgHeight; + + unsigned char *data = malloc(infoHeader.size); - fread(data, infoHeader.size, 1, rresFile); - - unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize); - - image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight); - - int pix = 0; - - for (int i = 0; i < (imgWidth*imgHeight*4); i += 4) + fread(data, infoHeader.size, 1, rresFile); + + unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize); + + image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight); + + 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++; + } + + free(imgData); + + free(data); + + TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height); + } + else + { + TraceLog(WARNING, "[%s] Required resource do not seem to be a valid IMAGE resource", rresName); + } + } + else { - 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++; + // Depending on type, skip the right amount of parameters + switch (infoHeader.type) + { + case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters + case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters + case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) + case 3: break; // TEXT: No parameters + case 4: break; // RAW: No parameters + default: break; + } + + // Jump DATA to read next infoHeader + fseek(rresFile, infoHeader.size, SEEK_CUR); } - - free(imgData); - - free(data); - } - else - { - printf("Required resource do not seem to be a valid IMAGE resource\n"); - exit(2); } } - else - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } + + fclose(rresFile); } - fclose(rresFile); - - if (!found) printf("Required resource id could not be found in the raylib Resource file!\n"); + if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); return image; } @@ -224,11 +246,33 @@ Image LoadImageFromRES(const char *rresName, int resId) Texture2D LoadTexture(const char *fileName) { Texture2D texture; - Image image; - - image = LoadImage(fileName); - texture = CreateTexture(image); - UnloadImage(image); + + if (strcmp(GetExtension(fileName),"dds") == 0) + { +#ifdef USE_OPENGL_11 + TraceLog(WARNING, "[%s] DDS file loading requires OpenGL 3.2+ or ES 2.0", fileName); +#else + ImageDDS image = LoadDDS(fileName); + + texture.glId = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.format); + + texture.width = image.width; + texture.height = image.height; + + if (texture.glId == 0) TraceLog(WARNING, "Compressed texture could not be loaded"); + else TraceLog(INFO, "Compressed texture loaded succesfully"); +#endif + } + else + { + Image image = LoadImage(fileName); + + if (image.pixels != NULL) + { + texture = CreateTexture(image); + UnloadImage(image); + } + } return texture; } @@ -259,7 +303,7 @@ void UnloadTexture(Texture2D texture) // Draw a Texture2D void DrawTexture(Texture2D texture, int posX, int posY, Color tint) { - DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY}, 0, 1.0f, tint); + DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0, 1.0f, tint); } // Draw a Texture2D with position defined as Vector2 @@ -276,9 +320,9 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc // NOTE: Rotation is applied before translation and scaling, even being called in inverse order... // NOTE: Rotation point is upper-left corner rlPushMatrix(); - rlTranslatef(position.x, position.y, 0.0); - rlScalef(scale, scale, 1.0f); + //rlTranslatef(position.x, position.y, 0.0); rlRotatef(rotation, 0, 0, 1); + rlScalef(scale, scale, 1.0f); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); @@ -390,7 +434,114 @@ Texture2D CreateTexture(Image image) texture.width = image.width; texture.height = image.height; + TraceLog(INFO, "Texture created succesfully"); + free(img); return texture; +} + +// Get the extension for a filename +static const char *GetExtension(const char *fileName) +{ + const char *dot = strrchr(fileName, '.'); + if(!dot || dot == fileName) return ""; + return (dot + 1); +} + +// Loading DDS image compressed data +ImageDDS LoadDDS(const char *fileName) +{ + // TODO: Review and expand DDS file loading to support uncompressed formats and new formats + + // DDS Pixel Format + typedef struct { + unsigned int size; + unsigned int flags; + unsigned int fourCC; + unsigned int rgbBitCount; + unsigned int rBitMask; + unsigned int gBitMask; + unsigned int bitMask; + unsigned int aBitMask; + } ddsPixelFormat; + + // DDS Header (124 bytes) + typedef struct { + unsigned int size; + unsigned int flags; + unsigned int height; + unsigned int width; + unsigned int pitchOrLinearSize; + unsigned int depth; + unsigned int mipMapCount; + unsigned int reserved1[11]; + ddsPixelFormat ddspf; + unsigned int caps; + unsigned int caps2; + unsigned int caps3; + unsigned int caps4; + unsigned int reserved2; + } ddsHeader; + + ImageDDS image; + ddsHeader header; + + FILE *ddsFile = fopen(fileName, "rb"); + + if (ddsFile == NULL) + { + TraceLog(WARNING, "DDS File could not be opened"); + } + else + { + // Verify the type of file + char filecode[4]; + + fread(filecode, 1, 4, ddsFile); + + if (strncmp(filecode, "DDS ", 4) != 0) + { + TraceLog(WARNING, "DDS File does not seem to be valid"); + fclose(ddsFile); + } + else + { + // Get the surface descriptor + fread(&header, sizeof(ddsHeader), 1, ddsFile); + + int height = header.height; + int width = header.width; + int linearSize = header.pitchOrLinearSize; + int mipMapCount = header.mipMapCount; + int fourCC = header.ddspf.fourCC; + + TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader)); + + TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, header.ddspf.size); + TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, header.ddspf.flags); + TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, fourCC); + + int bufsize; + + // Calculate data size, including all mipmaps + bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize; + + image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); + + fread(image.data, 1, bufsize, ddsFile); + + // Close file pointer + fclose(ddsFile); + + //int components = (fourCC == FOURCC_DXT1) ? 3 : 4; // Not required + + image.width = width; + image.height = height; + image.mipmaps = mipMapCount; + image.format = fourCC; + } + } + + return image; } \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 8c5d7240..6d2a4f30 100644 --- a/src/utils.c +++ b/src/utils.c @@ -29,7 +29,8 @@ #include "utils.h" #include // malloc(), free() -#include // printf() +#include // printf(), fprintf() +#include // Used for functions with variable number of parameters (TraceLog()) //#include // String management functions: strlen(), strrchr(), strcmp() #define STB_IMAGE_WRITE_IMPLEMENTATION @@ -37,6 +38,15 @@ #include "stb_image_write.h" // Create PNG file #include "tinfl.c" +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static FILE *logstream = NULL; + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Utilities +//---------------------------------------------------------------------------------- + // Data decompression function // NOTE: Allocated data MUST be freed! unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize) @@ -50,28 +60,28 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, // Check correct memory allocation if (!pUncomp) { - printf("Out of memory!\n"); - return NULL; + TraceLog(WARNING, "Out of memory while decompressing data"); } - - // Decompress data - tempUncompSize = tinfl_decompress_mem_to_mem(pUncomp, (size_t)uncompSize, data, compSize, 1); - - if (tempUncompSize == -1) + else { - printf("Decompression failed!\n"); - free(pUncomp); - return NULL; - } - - if (uncompSize != (int)tempUncompSize) - { - printf("WARNING! Expected uncompressed size do not match! Data may be corrupted!\n"); - printf(" -- Expected uncompressed size: %i\n", uncompSize); - printf(" -- Returned uncompressed size: %i\n", tempUncompSize); - } + // Decompress data + tempUncompSize = tinfl_decompress_mem_to_mem(pUncomp, (size_t)uncompSize, data, compSize, 1); + + if (tempUncompSize == -1) + { + TraceLog(WARNING, "Data decompression failed"); + free(pUncomp); + } + + if (uncompSize != (int)tempUncompSize) + { + TraceLog(WARNING, "Expected uncompressed size do not match, data may be corrupted"); + TraceLog(WARNING, " -- Expected uncompressed size: %i", uncompSize); + TraceLog(WARNING, " -- Returned uncompressed size: %i", tempUncompSize); + } - printf("Decompressed from %u bytes to %u bytes\n", (mz_uint32)compSize, (mz_uint32)tempUncompSize); + TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); + } return pUncomp; } @@ -124,4 +134,100 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he void WritePNG(const char *fileName, unsigned char *imgData, int width, int height) { stbi_write_png(fileName, width, height, 4, imgData, width*4); // It WORKS!!! -} \ No newline at end of file +} + +// Outputs a trace log message (INFO, ERROR, WARNING) +// NOTE: If a file has been init, output log is written there +void TraceLog(int msgType, const char *text, ...) +{ + // TODO: This function requires some refactoring... + + // NOTE: If trace log file has been set, stdout is being redirected to a file + va_list args; + int traceDebugMsgs = 1; + +#ifdef DO_NOT_TRACE_DEBUG_MSGS + traceDebugMsgs = 0; +#endif + + if (logstream != NULL) + { + switch(msgType) + { + case 0: fprintf(logstream, "INFO: "); break; + case 1: fprintf(logstream, "ERROR: "); break; + case 2: fprintf(logstream, "WARNING: "); break; + case 3: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break; + default: break; + } + + if (msgType == 3) + { + if (traceDebugMsgs) + { + va_start(args, text); + vfprintf(logstream, text, args); + va_end(args); + + fprintf(logstream, "\n"); + } + } + else + { + va_start(args, text); + vfprintf(logstream, text, args); + va_end(args); + + fprintf(logstream, "\n"); + } + } + else + { + switch(msgType) + { + case 0: fprintf(stdout, "INFO: "); break; + case 1: fprintf(stdout, "ERROR: "); break; + case 2: fprintf(stdout, "WARNING: "); break; + case 3: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break; + default: break; + } + + if (msgType == 3) + { + if (traceDebugMsgs) + { + va_start(args, text); + vfprintf(stdout, text, args); + va_end(args); + + fprintf(stdout, "\n"); + } + } + else + { + va_start(args, text); + vfprintf(stdout, text, args); + va_end(args); + + fprintf(stdout, "\n"); + } + } + + if (msgType == 1) exit(1); // If ERROR message, exit program +} + +// Inits a trace log file +void InitTraceLogFile(const char *logFileName) +{ + // stdout redirected to stream file + FILE *logstream = fopen(logFileName, "w"); + + if (logstream == NULL) TraceLog(WARNING, "Unable to open log file"); +} + +// Closes the trace log file +void CloseTraceLogFile() +{ + if (logstream != NULL) fclose(logstream); +} + diff --git a/src/utils.h b/src/utils.h index 53241aff..a887beef 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,13 +32,15 @@ //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- -//... +//#define DO_NOT_TRACE_DEBUG_MSGS // Use this define to avoid DEBUG tracing //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef enum { IMAGE, SOUND, MODEL, TEXT, RAW } DataType; +typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; + // One resource info header, every resource includes this header (8 byte) typedef struct { unsigned short id; // Resource unique identifier (2 byte) @@ -61,9 +63,14 @@ extern "C" { // Prevents name mangling of functions // Module Functions Declaration //---------------------------------------------------------------------------------- unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); + void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height); void WritePNG(const char *fileName, unsigned char *imgData, int width, int height); +void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message +void InitTraceLogFile(const char *logFileName); // Inits a trace log file +void CloseTraceLogFile(); // Closes the trace log file + #ifdef __cplusplus } #endif -- cgit v1.2.3