diff options
| author | victorfisac <[email protected]> | 2017-03-06 09:47:08 +0100 |
|---|---|---|
| committer | victorfisac <[email protected]> | 2017-03-06 09:47:08 +0100 |
| commit | f9277f216372179560c560427beccdd2e5c5d094 (patch) | |
| tree | 8d3858c978f2b36ea8912f25e3cbe6fa56952aff /src/rlgl.c | |
| parent | ce56fcb1eda06385b88c1a906f0968d742ff8130 (diff) | |
| parent | c05701253e0a4eda211a0d7ced74ae29d6585917 (diff) | |
| download | raylib-f9277f216372179560c560427beccdd2e5c5d094.tar.gz raylib-f9277f216372179560c560427beccdd2e5c5d094.zip | |
Merge remote-tracking branch 'refs/remotes/raysan5/master'
Diffstat (limited to 'src/rlgl.c')
| -rw-r--r-- | src/rlgl.c | 3882 |
1 files changed, 2422 insertions, 1460 deletions
@@ -2,12 +2,57 @@ * * rlgl - raylib OpenGL abstraction layer * -* raylib now uses OpenGL 1.1 style functions (rlVertex) that are mapped to selected OpenGL version: -* OpenGL 1.1 - Direct map rl* -> gl* -* OpenGL 3.3 - Vertex data is stored in VAOs, call rlglDraw() to render -* OpenGL ES 2 - Vertex data is stored in VBOs or VAOs (when available), call rlglDraw() to render +* DESCRIPTION: * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* rlgl allows usage of OpenGL 1.1 style functions (rlVertex) that are internally mapped to +* selected OpenGL version (1.1, 2.1, 3.3 Core, ES 2.0). +* +* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal +* VBO buffers (and VAOs if available). It requires calling 3 functions: +* rlglInit() - Initialize internal buffers and auxiliar resources +* rlglDraw() - Process internal buffers and send required draw calls +* rlglClose() - De-initialize internal buffers data and other auxiliar resources +* +* CONFIGURATION: +* +* #define GRAPHICS_API_OPENGL_11 +* Use OpenGL 1.1 backend +* +* #define GRAPHICS_API_OPENGL_21 +* Use OpenGL 2.1 backend +* +* #define GRAPHICS_API_OPENGL_33 +* Use OpenGL 3.3 Core profile backend +* +* #define GRAPHICS_API_OPENGL_ES2 +* Use OpenGL ES 2.0 backend +* +* #define RLGL_STANDALONE +* Use rlgl as standalone library (no raylib dependency) +* +* #define RLGL_NO_DISTORTION_SHADER +* Avoid stereo rendering distortion sahder (shader_distortion.h) inclusion +* +* #define SUPPORT_SHADER_DEFAULT / ENABLE_SHADER_DEFAULT +* +* #define SUPPORT_SHADER_DISTORTION +* +* +* #define SUPPORT_OCULUS_RIFT_CV1 / RLGL_OCULUS_SUPPORT +* Enable Oculus Rift CV1 functionality +* +* #define SUPPORT_STEREO_RENDERING +* +* #define RLGL_NO_DEFAULT_SHADER +* +* DEPENDENCIES: +* raymath - 3D math functionality (Vector3, Matrix, Quaternion) +* GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -28,38 +73,63 @@ #include "rlgl.h" -#include <stdio.h> // Standard input / output lib -#include <stdlib.h> // Declares malloc() and free() for memory management, rand() -#include <string.h> // Declares strcmp(), strlen(), strtok() +#include <stdio.h> // Required for: fopen(), fclose(), fread()... [Used only on LoadText()] +#include <stdlib.h> // Required for: malloc(), free(), rand() +#include <string.h> // Required for: strcmp(), strlen(), strtok() +#include <math.h> // Required for: atan2() -#include "raymath.h" // Required for Vector3 and Matrix functions +#ifndef RLGL_STANDALONE + #include "raymath.h" // Required for: Vector3 and Matrix functions +#endif #if defined(GRAPHICS_API_OPENGL_11) - #ifdef __APPLE__ // OpenGL include for OSX - #include <OpenGL/gl.h> + #ifdef __APPLE__ + #include <OpenGL/gl.h> // OpenGL 1.1 library for OSX #else - #include <GL/gl.h> // Basic OpenGL include + #include <GL/gl.h> // OpenGL 1.1 library #endif #endif +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 +#endif + #if defined(GRAPHICS_API_OPENGL_33) - #ifdef __APPLE__ // OpenGL include for OSX - #include <OpenGL/gl3.h> + #ifdef __APPLE__ + #include <OpenGL/gl3.h> // OpenGL 3 library for OSX #else - //#define GLEW_STATIC - //#include <GL/glew.h> // GLEW header, includes OpenGL headers - #include "glad.h" // glad header, includes OpenGL headers + #define GLAD_IMPLEMENTATION + #if defined(RLGL_STANDALONE) + #include "glad.h" // GLAD extensions loading library, includes OpenGL headers + #else + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #endif #endif #endif #if defined(GRAPHICS_API_OPENGL_ES2) - #include <EGL/egl.h> - #include <GLES2/gl2.h> - #include <GLES2/gl2ext.h> + #include <EGL/egl.h> // EGL library + #include <GLES2/gl2.h> // OpenGL ES 2.0 library + #include <GLES2/gl2ext.h> // OpenGL ES 2.0 extensions library #endif #if defined(RLGL_STANDALONE) - #include <stdarg.h> // Used for functions with variable number of parameters (TraceLog()) + #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()] +#endif + +#if !defined(GRAPHICS_API_OPENGL_11) && !defined(RLGL_NO_DISTORTION_SHADER) + #include "shader_distortion.h" // Distortion shader to be embedded +#endif + +//#define RLGL_OCULUS_SUPPORT // Enable Oculus Rift code +#if defined(RLGL_OCULUS_SUPPORT) + #include "external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h" // Oculus SDK for OpenGL +#endif + +#if defined(RLGL_STANDALONE) + #define OCULUSAPI +#else + #define OCULUSAPI static #endif //---------------------------------------------------------------------------------- @@ -108,90 +178,119 @@ #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 #endif +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif + #if defined(GRAPHICS_API_OPENGL_11) #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #define glClearDepth glClearDepthf + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER +#endif + +// Default vertex attribute names on shader to set location points +#define DEFAULT_ATTRIB_POSITION_NAME "vertexPosition" // shader-location = 0 +#define DEFAULT_ATTRIB_TEXCOORD_NAME "vertexTexCoord" // shader-location = 1 +#define DEFAULT_ATTRIB_NORMAL_NAME "vertexNormal" // shader-location = 2 +#define DEFAULT_ATTRIB_COLOR_NAME "vertexColor" // shader-location = 3 +#define DEFAULT_ATTRIB_TANGENT_NAME "vertexTangent" // shader-location = 4 +#define DEFAULT_ATTRIB_TEXCOORD2_NAME "vertexTexCoord2" // shader-location = 5 + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -// Vertex buffer (position + color arrays) -// NOTE: Used for lines and triangles VAOs -typedef struct { - int vCounter; - int cCounter; - float *vertices; // 3 components per vertex - unsigned char *colors; // 4 components per vertex -} VertexPositionColorBuffer; - -// Vertex buffer (position + texcoords + color arrays) -// NOTE: Not used -typedef struct { - int vCounter; - int tcCounter; - int cCounter; - float *vertices; // 3 components per vertex - float *texcoords; // 2 components per vertex - unsigned char *colors; // 4 components per vertex -} VertexPositionColorTextureBuffer; - -// Vertex buffer (position + texcoords + normals arrays) -// NOTE: Not used -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 - //short *normals; // NOTE: Less data load... but padding issues and normalizing required! -} VertexPositionTextureNormalBuffer; - -// Vertex buffer (position + texcoords + colors + indices arrays) -// NOTE: Used for quads VAO -typedef struct { - int vCounter; - int tcCounter; - int cCounter; - float *vertices; // 3 components per vertex - float *texcoords; // 2 components per vertex - unsigned char *colors; // 4 components per vertex +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +typedef struct DynamicBuffer { + int vCounter; // vertex position counter to process (and draw) from full buffer + int tcCounter; // vertex texcoord counter to process (and draw) from full buffer + int cCounter; // vertex color counter to process (and draw) from full buffer + float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - unsigned int *indices; // 6 indices per quad (could be int) + unsigned int *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad) #elif defined(GRAPHICS_API_OPENGL_ES2) - unsigned short *indices; // 6 indices per quad (must be short) + unsigned short *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad) // NOTE: 6*2 byte = 12 byte, not alignment problem! #endif -} VertexPositionColorTextureIndexBuffer; + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} DynamicBuffer; // Draw call type // NOTE: Used to track required draw-calls, organized by texture -typedef struct { - GLuint textureId; +typedef struct DrawCall { int vertexCount; - // TODO: Store draw state -> blending mode, shader -} DrawCall; + GLuint vaoId; + GLuint textureId; + GLuint shaderId; -// pixel type (same as Color type) -// NOTE: Used exclusively in mipmap generation functions -typedef struct { - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -} pixel; - -// Framebuffer Object type -typedef struct { - GLuint id; - GLuint colorTextureId; - GLuint depthTextureId; -} FBO; + Matrix projection; + Matrix modelview; -#if defined(RLGL_STANDALONE) -typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; + // TODO: Store additional draw state data + //int blendMode; + //Guint fboId; +} DrawCall; + +// Head-Mounted-Display device parameters +typedef struct VrDeviceInfo { + int hResolution; // HMD horizontal resolution in pixels + int vResolution; // HMD vertical resolution in pixels + float hScreenSize; // HMD horizontal size in meters + float vScreenSize; // HMD vertical size in meters + float vScreenCenter; // HMD screen center in meters + float eyeToScreenDistance; // HMD distance between eye and display in meters + float lensSeparationDistance; // HMD lens separation distance in meters + float interpupillaryDistance; // HMD IPD (distance between pupils) in meters + float distortionK[4]; // HMD lens distortion constant parameters + float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters +} VrDeviceInfo; + +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + RenderTexture2D stereoFbo; // VR stereo rendering framebuffer + Shader distortionShader; // VR stereo rendering distortion shader + //Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices +} VrStereoConfig; + +#if defined(RLGL_OCULUS_SUPPORT) +typedef struct OculusBuffer { + ovrTextureSwapChain textureChain; + GLuint depthId; + GLuint fboId; + int width; + int height; +} OculusBuffer; + +typedef struct OculusMirror { + ovrMirrorTexture texture; + GLuint fboId; + int width; + int height; +} OculusMirror; + +typedef struct OculusLayer { + ovrViewScaleDesc viewScaleDesc; + ovrLayerEyeFov eyeLayer; // layer 0 + //ovrLayerQuad quadLayer; // TODO: layer 1: '2D' quad for GUI + Matrix eyeProjections[2]; + int width; + int height; +} OculusLayer; #endif //---------------------------------------------------------------------------------- @@ -210,23 +309,11 @@ static DrawMode currentDrawMode; static float currentDepth = -1.0f; -// Vertex arrays for lines, triangles and quads -static VertexPositionColorBuffer lines; // No texture support -static VertexPositionColorBuffer triangles; // No texture support -static VertexPositionColorTextureIndexBuffer quads; - -// Shader Programs -static Shader defaultShader, simpleShader; -static Shader currentShader; // By default, defaultShader - -// Vertex Array Objects (VAO) -static GLuint vaoLines, vaoTriangles, vaoQuads; - -// Vertex Buffer Objects (VBO) -static GLuint linesBuffer[2]; -static GLuint trianglesBuffer[2]; -static GLuint quadsBuffer[4]; +static DynamicBuffer lines; // Default dynamic buffer for lines data +static DynamicBuffer triangles; // Default dynamic buffer for triangles data +static DynamicBuffer quads; // Default dynamic buffer for quads data (used to draw textures) +// Default buffers draw calls static DrawCall *draws; static int drawsCounter; @@ -235,27 +322,46 @@ static Vector3 *tempBuffer; static int tempBufferCount = 0; static bool useTempBuffer = false; -// Flags for supported extensions -static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) +// Shader Programs +static Shader defaultShader; // Basic shader, support vertex color and diffuse texture +static Shader currentShader; // Shader to be used on rendering (by default, defaultShader) -// Compressed textures support flags -//static bool texCompDXTSupported = false; // DDS texture compression support -static bool texCompETC1Supported = false; // ETC1 texture compression support -static bool texCompETC2Supported = false; // ETC2/EAC texture compression support -static bool texCompPVRTSupported = false; // PVR texture compression support -static bool texCompASTCSupported = false; // ASTC texture compression support +// Extension supported flag: VAO +static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) -// Framebuffer object and texture -static FBO postproFbo; -static Model postproQuad; +// Extension supported flag: Compressed textures +static bool texCompETC1Supported = false; // ETC1 texture compression support +static bool texCompETC2Supported = false; // ETC2/EAC texture compression support +static bool texCompPVRTSupported = false; // PVR texture compression support +static bool texCompASTCSupported = false; // ASTC texture compression support +#endif -// Shaders related variables -static bool enabledPostpro = false; +// Extension supported flag: Anisotropic filtering +static bool texAnisotropicFilterSupported = false; // Anisotropic texture filtering support +static float maxAnisotropicLevel = 0.0f; // Maximum anisotropy level supported (minimum is 2.0f) + +// Extension supported flag: Clamp mirror wrap mode +static bool texClampMirrorSupported = false; // Clamp mirror wrap mode supported + +#if defined(RLGL_OCULUS_SUPPORT) +// OVR device variables +static ovrSession session; // Oculus session (pointer to ovrHmdStruct) +static ovrHmdDesc hmdDesc; // Oculus device descriptor parameters +static ovrGraphicsLuid luid; // Oculus locally unique identifier for the program (64 bit) +static OculusLayer layer; // Oculus drawing layer (similar to photoshop) +static OculusBuffer buffer; // Oculus internal buffers (texture chain and fbo) +static OculusMirror mirror; // Oculus mirror texture and fbo +static unsigned int frameIndex = 0; // Oculus frames counter, used to discard frames from chain #endif -// Compressed textures support flags -static bool texCompDXTSupported = false; // DDS texture compression support -static bool npotSupported = false; // NPOT textures full support +// VR global variables +static VrDeviceInfo hmd; // Current VR device info +static VrStereoConfig vrConfig; // VR stereo configuration for simulator +static bool vrDeviceReady = false; // VR device ready flag +static bool vrSimulator = false; // VR simulator enabled flag +static bool vrEnabled = false; // VR experience enabled (device or simulator) +static bool vrRendering = true; // VR stereo rendering enabled/disabled flag + // NOTE: This flag is useful to render data over stereo image (i.e. FPS) #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) @@ -265,53 +371,69 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; //static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: Fails in WebGL, omitted #endif -// Save screen size data (render size), required for postpro quad -static int screenWidth, screenHeight; +// Compressed textures support flags +static bool texCompDXTSupported = false; // DDS texture compression support +static bool npotSupported = false; // NPOT textures full support -static int blendMode = 0; +static int blendMode = 0; // Track current blending mode // White texture useful for plain color polys (required by shader) -// NOTE: It's required in shapes and models modules! -unsigned int whiteTexture; +static unsigned int whiteTexture; + +// Default framebuffer size (required by Oculus device) +static int screenWidth; // Default framebuffer width +static int screenHeight; // Default framebuffer height //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static Shader LoadDefaultShader(void); -static Shader LoadSimpleShader(void); -static void InitializeBuffers(void); -static void InitializeBuffersGPU(void); -static void UpdateBuffers(void); -static char *TextFileRead(char *fn); - static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); +static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShaderStr); // Load custom shader strings and return program id + +static Shader LoadDefaultShader(void); // Load default shader (just vertex positioning and texture coloring) +static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) +static void UnloadDefaultShader(void); // Unload default shader + +static void LoadDefaultBuffers(void); // Load default internal buffers (lines, triangles, quads) +static void UpdateDefaultBuffers(void); // Update default internal buffers (VAOs/VBOs) with vertex data +static void DrawDefaultBuffers(int eyesCount); // Draw default internal buffers vertex data +static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU -FBO rlglLoadFBO(int width, int height); -void rlglUnloadFBO(FBO fbo); +// Configure stereo rendering (including distortion shader) with HMD device parameters +static void SetStereoConfig(VrDeviceInfo info); + +// Set internal projection and modelview matrix depending on eyes tracking data +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); #endif -#if defined(GRAPHICS_API_OPENGL_11) -static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); -static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); +#if defined(RLGL_OCULUS_SUPPORT) +#if !defined(RLGL_STANDALONE) +static bool InitOculusDevice(void); // Initialize Oculus device (returns true if success) +static void CloseOculusDevice(void); // Close Oculus device +static void UpdateOculusTracking(Camera *camera); // Update Oculus head position-orientation tracking +static void BeginOculusDrawing(void); // Setup Oculus buffers for drawing +static void EndOculusDrawing(void); // Finish Oculus drawing and blit framebuffer to mirror #endif -#if defined(RLGL_STANDALONE) -static void TraceLog(int msgType, const char *text, ...); +static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height); // Load Oculus required buffers +static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer); // Unload texture required buffers +static OculusMirror LoadOculusMirror(ovrSession session, int width, int height); // Load Oculus mirror buffers +static void UnloadOculusMirror(ovrSession session, OculusMirror mirror); // Unload Oculus mirror buffers +static void BlitOculusMirror(ovrSession session, OculusMirror mirror); // Copy Oculus screen buffer to mirror texture +static OculusLayer InitOculusLayer(ovrSession session); // Init Oculus layer (similar to photoshop) +static Matrix FromOvrMatrix(ovrMatrix4f ovrM); // Convert from Oculus ovrMatrix4f struct to raymath Matrix struct #endif -#if defined(GRAPHICS_API_OPENGL_ES2) -// NOTE: strdup() functions replacement (not C99, POSIX function, not available on emscripten) -// Duplicates a string, returning an identical malloc'd string -char *mystrdup(const char *str) -{ - size_t len = strlen(str) + 1; - void *newstr = malloc(len); - if (newstr == NULL) return NULL; - return (char *)memcpy(newstr, str, len); -} +#if defined(GRAPHICS_API_OPENGL_11) +static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); +#endif + +#if defined(RLGL_STANDALONE) +float *MatrixToFloat(Matrix mat); // Converts Matrix to float array #endif //---------------------------------------------------------------------------------- @@ -333,14 +455,14 @@ void rlMatrixMode(int mode) } } -void rlFrustum(double left, double right, double bottom, double top, double near, double far) +void rlFrustum(double left, double right, double bottom, double top, double zNear, double zFar) { - glFrustum(left, right, bottom, top, near, far); + glFrustum(left, right, bottom, top, zNear, zFar); } -void rlOrtho(double left, double right, double bottom, double top, double near, double far) +void rlOrtho(double left, double right, double bottom, double top, double zNear, double zFar) { - glOrtho(left, right, bottom, top, near, far); + glOrtho(left, right, bottom, top, zNear, zFar); } void rlPushMatrix(void) { glPushMatrix(); } @@ -412,7 +534,6 @@ void rlRotatef(float angleDeg, float x, float y, float z) Vector3 axis = (Vector3){ x, y, z }; VectorNormalize(&axis); matRotation = MatrixRotate(axis, angleDeg*DEG2RAD); - MatrixTranspose(&matRotation); *currentMatrix = MatrixMultiply(*currentMatrix, matRotation); @@ -459,6 +580,13 @@ void rlOrtho(double left, double right, double bottom, double top, double near, #endif +// Set the viewport area (transformation from normalized device coordinates to window coordinates) +// NOTE: Updates global variables: screenWidth, screenHeight +void rlViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Vertex level operations //---------------------------------------------------------------------------------- @@ -593,7 +721,7 @@ void rlEnd(void) } break; default: break; } - + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, // as well as depth buffer bit-depth (16bit or 24bit or 32bit) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) @@ -617,7 +745,7 @@ void rlVertex3f(float x, float y, float z) case RL_LINES: { // Verify that MAX_LINES_BATCH limit not reached - if (lines.vCounter / 2 < MAX_LINES_BATCH) + if (lines.vCounter/2 < MAX_LINES_BATCH) { lines.vertices[3*lines.vCounter] = x; lines.vertices[3*lines.vCounter + 1] = y; @@ -631,7 +759,7 @@ void rlVertex3f(float x, float y, float z) case RL_TRIANGLES: { // Verify that MAX_TRIANGLES_BATCH limit not reached - if (triangles.vCounter / 3 < MAX_TRIANGLES_BATCH) + if (triangles.vCounter/3 < MAX_TRIANGLES_BATCH) { triangles.vertices[3*triangles.vCounter] = x; triangles.vertices[3*triangles.vCounter + 1] = y; @@ -645,7 +773,7 @@ void rlVertex3f(float x, float y, float z) case RL_QUADS: { // Verify that MAX_QUADS_BATCH limit not reached - if (quads.vCounter / 4 < MAX_QUADS_BATCH) + if (quads.vCounter/4 < MAX_QUADS_BATCH) { quads.vertices[3*quads.vCounter] = x; quads.vertices[3*quads.vCounter + 1] = y; @@ -777,20 +905,111 @@ void rlDisableTexture(void) #if defined(GRAPHICS_API_OPENGL_11) glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); +#else + // NOTE: If quads batch limit is reached, + // we force a draw call and next batch starts + if (quads.vCounter/4 >= MAX_QUADS_BATCH) rlglDraw(); +#endif +} + +// Set texture parameters (wrap mode/filter mode) +void rlTextureParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_2D, id); + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if ((value == RL_WRAP_CLAMP_MIRROR) && !texClampMirrorSupported) TraceLog(WARNING, "Clamp mirror wrap mode not supported"); + else glTexParameteri(GL_TEXTURE_2D, param, value); + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; + case RL_TEXTURE_ANISOTROPIC_FILTER: + { + if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (maxAnisotropicLevel > 0.0f) + { + TraceLog(WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TraceLog(WARNING, "Anisotropic filtering not supported"); + } break; + default: break; + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Enable rendering to texture (fbo) +void rlEnableRenderTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + //glDisable(GL_CULL_FACE); // Allow double side drawing for texture flipping + //glCullFace(GL_FRONT); +#endif +} + +// Disable rendering to texture +void rlDisableRenderTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + //glEnable(GL_CULL_FACE); + //glCullFace(GL_BACK); +#endif +} + +// Enable depth test +void rlEnableDepthTest(void) +{ + glEnable(GL_DEPTH_TEST); +} + +// Disable depth test +void rlDisableDepthTest(void) +{ + glDisable(GL_DEPTH_TEST); +} + +// Enable wire mode +void rlEnableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif +} + +// Disable wire mode +void rlDisableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } // Unload texture from GPU memory void rlDeleteTextures(unsigned int id) { - glDeleteTextures(1, &id); + if (id != 0) glDeleteTextures(1, &id); } -// Enable rendering to postprocessing FBO -void rlEnablePostproFBO() +// Unload render texture from GPU memory +void rlDeleteRenderTextures(RenderTexture2D target) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindFramebuffer(GL_FRAMEBUFFER, postproFbo.id); + if (target.id != 0) glDeleteFramebuffers(1, &target.id); + if (target.texture.id != 0) glDeleteTextures(1, &target.texture.id); + if (target.depth.id != 0) glDeleteTextures(1, &target.depth.id); + + TraceLog(INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); #endif } @@ -798,7 +1017,7 @@ void rlEnablePostproFBO() void rlDeleteShader(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteProgram(id); + if (id != 0) glDeleteProgram(id); #endif } @@ -806,7 +1025,11 @@ void rlDeleteShader(unsigned int id) void rlDeleteVertexArrays(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vaoSupported) glDeleteVertexArrays(1, &id); + if (vaoSupported) + { + if (id != 0) glDeleteVertexArrays(1, &id); + TraceLog(INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); + } #endif } @@ -814,7 +1037,11 @@ void rlDeleteVertexArrays(unsigned int id) void rlDeleteBuffers(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteBuffers(1, &id); + if (id != 0) + { + glDeleteBuffers(1, &id); + if (!vaoSupported) TraceLog(INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + } #endif } @@ -842,6 +1069,8 @@ int rlGetVersion(void) { #if defined(GRAPHICS_API_OPENGL_11) return OPENGL_11; +#elif defined(GRAPHICS_API_OPENGL_21) + return OPENGL_21; #elif defined(GRAPHICS_API_OPENGL_33) return OPENGL_33; #elif defined(GRAPHICS_API_OPENGL_ES2) @@ -853,12 +1082,12 @@ int rlGetVersion(void) // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- -// Init OpenGL 3.3+ required data -void rlglInit(void) +// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states +void rlglInit(int width, int height) { // Check OpenGL information and capabilities //------------------------------------------------------------------------------ - + // Print current OpenGL and GLSL version TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); @@ -869,14 +1098,14 @@ void rlglInit(void) //int maxTexSize; //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); //TraceLog(INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); - + //GL_MAX_TEXTURE_IMAGE_UNITS //GL_MAX_VIEWPORT_DIMS //int numAuxBuffers; //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); //TraceLog(INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); - + //GLint numComp = 0; //GLint format[32] = { 0 }; //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -884,7 +1113,7 @@ void rlglInit(void) //for (int i = 0; i < numComp; i++) TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); // NOTE: We don't need that much data on screen... right now... - + #if defined(GRAPHICS_API_OPENGL_11) //TraceLog(INFO, "OpenGL 1.1 (or driver default) profile initialized"); #endif @@ -892,30 +1121,40 @@ void rlglInit(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Get supported extensions list GLint numExt = 0; - + #if defined(GRAPHICS_API_OPENGL_33) // NOTE: On OpenGL 3.3 VAO and NPOT are supported by default vaoSupported = true; npotSupported = true; - // NOTE: We don't need to check again supported extensions but we do (in case GLEW is replaced sometime) // We get a list of available extensions and we check for some of them (compressed textures) + // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + +#ifdef _MSC_VER + const char **extList = malloc(sizeof(const char *)*numExt); +#else const char *extList[numExt]; - +#endif + for (int i = 0; i < numExt; i++) extList[i] = (char *)glGetStringi(GL_EXTENSIONS, i); - + #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string - + // NOTE: We have to duplicate string because glGetString() returns a const value // If not duplicated, it fails in some systems (Raspberry Pi) - char *extensionsDup = mystrdup(extensions); - + // Equivalent to function: char *strdup(const char *str) + char *extensionsDup; + size_t len = strlen(extensions) + 1; + void *newstr = malloc(len); + if (newstr == NULL) extensionsDup = NULL; + extensionsDup = (char *)memcpy(newstr, extensions, len); + // NOTE: String could be splitted using strtok() function (string.h) // NOTE: strtok() modifies the received string, it can not be const - + char *extList[512]; // Allocate 512 strings pointers (2 KB) extList[numExt] = strtok(extensionsDup, " "); @@ -925,9 +1164,9 @@ void rlglInit(void) numExt++; extList[numExt] = strtok(NULL, " "); } - + free(extensionsDup); // Duplicated string must be deallocated - + numExt -= 1; #endif @@ -945,26 +1184,28 @@ void rlglInit(void) if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) { vaoSupported = true; - - // The extension is supported by our hardware and driver, try to get related functions pointers + + // The extension is supported by our hardware and driver, try to get related functions pointers // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted } - + // Check NPOT textures support // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) npotSupported = true; -#endif - +#endif + // DDS texture compression support if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true; - + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true; + // ETC1 texture compression support - if (strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) texCompETC1Supported = true; + if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) texCompETC1Supported = true; // ETC2/EAC texture compression support if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) texCompETC2Supported = true; @@ -974,12 +1215,26 @@ void rlglInit(void) // ASTC texture compression support if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; + + // Anisotropic texture filter support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) + { + texAnisotropicFilterSupported = true; + glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + } + + // Clamp mirror wrap mode supported + if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) texClampMirrorSupported = true; } - + +#ifdef _MSC_VER + free(extList); +#endif + #if defined(GRAPHICS_API_OPENGL_ES2) if (vaoSupported) TraceLog(INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); else TraceLog(WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); - + if (npotSupported) TraceLog(INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); else TraceLog(WARNING, "[EXTENSION] NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); #endif @@ -990,21 +1245,13 @@ void rlglInit(void) if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported"); if (texCompASTCSupported) TraceLog(INFO, "[EXTENSION] ASTC compressed textures supported"); + if (texAnisotropicFilterSupported) TraceLog(INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel); + if (texClampMirrorSupported) TraceLog(INFO, "[EXTENSION] Clamp mirror wrap texture mode supported"); + // Initialize buffers, default shaders and default textures //---------------------------------------------------------- - - // Set default draw mode - currentDrawMode = RL_TRIANGLES; - // Reset projection and modelview matrices - projection = MatrixIdentity(); - modelview = MatrixIdentity(); - currentMatrix = &modelview; - - // Initialize matrix stack - for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); - - // Create default white texture for plain colors (required by shader) + // Init default white texture unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) whiteTexture = rlglLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); @@ -1012,15 +1259,12 @@ void rlglInit(void) if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); else TraceLog(WARNING, "Base white texture could not be loaded"); - // Init default Shader (Custom for GL 3.3 and ES2) + // Init default Shader (customized for GL 3.3 and ES2) defaultShader = LoadDefaultShader(); - simpleShader = LoadSimpleShader(); - //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok - currentShader = defaultShader; - InitializeBuffers(); // Init vertex arrays - InitializeBuffersGPU(); // Init VBO and VAO + // Init default vertex arrays buffers (lines, triangles, quads) + LoadDefaultBuffers(); // Init temp vertex buffer, used when transformation required (translate, rotate, scale) tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); @@ -1038,202 +1282,63 @@ void rlglInit(void) drawsCounter = 1; draws[drawsCounter - 1].textureId = whiteTexture; -#endif -} - -// Init postpro system -// NOTE: Uses global variables screenWidth and screenHeight -// Modifies global variables: postproFbo, postproQuad -void rlglInitPostpro(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - postproFbo = rlglLoadFBO(screenWidth, screenHeight); + currentDrawMode = RL_TRIANGLES; // Set default draw mode - if (postproFbo.id > 0) - { - // Create a simple quad model to render fbo texture - Mesh quad; - - quad.vertexCount = 6; - - float w = (float)screenWidth; - float h = (float)screenHeight; - - float quadPositions[6*3] = { w, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, h, 0.0f, 0.0f, h, 0.0f, w, h, 0.0f, w, 0.0f, 0.0f }; - float quadTexcoords[6*2] = { 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }; - float quadNormals[6*3] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; - unsigned char quadColors[6*4] = { 255 }; - - quad.vertices = quadPositions; - quad.texcoords = quadTexcoords; - quad.normals = quadNormals; - quad.colors = quadColors; - - postproQuad = rlglLoadModel(quad); - - // NOTE: postproFbo.colorTextureId must be assigned to postproQuad model shader - } -#endif -} + // Init internal matrix stack (emulating OpenGL 1.1) + for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); -// Load a framebuffer object -FBO rlglLoadFBO(int width, int height) -{ - FBO fbo; - fbo.id = 0; + // Init internal projection and modelview matrices + projection = MatrixIdentity(); + modelview = MatrixIdentity(); + currentMatrix = &modelview; +#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Create the texture that will serve as the color attachment for the framebuffer - glGenTextures(1, &fbo.colorTextureId); - glBindTexture(GL_TEXTURE_2D, fbo.colorTextureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, 0); + // Initialize OpenGL default states + //---------------------------------------------------------- - // Create the renderbuffer that will serve as the depth attachment for the framebuffer. - glGenRenderbuffers(1, &fbo.depthTextureId); - glBindRenderbuffer(GL_RENDERBUFFER, fbo.depthTextureId); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); - - // NOTE: We can also use a texture for depth buffer (GL_ARB_depth_texture/GL_OES_depth_texture extensions) - // A renderbuffer is simpler than a texture and could offer better performance on embedded devices -/* - glGenTextures(1, &fbo.depthTextureId); - glBindTexture(GL_TEXTURE_2D, fbo.depthTextureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, 0); -*/ - // Create the framebuffer object - glGenFramebuffers(1, &fbo.id); - glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + // Init state: Depth test + glDepthFunc(GL_LEQUAL); // Type of depth testing to apply + glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) - // Attach color texture and depth renderbuffer to FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.colorTextureId, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo.depthTextureId); + // Init state: Blending mode + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) + glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + // Init state: Culling + // NOTE: All shapes/models triangles are drawn CCW + glCullFace(GL_BACK); // Cull the back face (default) + glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) + glEnable(GL_CULL_FACE); // Enable backface culling - if (status != GL_FRAMEBUFFER_COMPLETE) - { - TraceLog(WARNING, "Framebuffer object could not be created..."); - - switch(status) - { - case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break; -#if defined(GRAPHICS_API_OPENGL_ES2) - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(WARNING, "Framebuffer incomplete dimensions"); break; -#endif - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break; - default: break; - } - - glDeleteTextures(1, &fbo.colorTextureId); - glDeleteTextures(1, &fbo.depthTextureId); - } - else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", fbo); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); +#if defined(GRAPHICS_API_OPENGL_11) + // Init state: Color hints (deprecated in OpenGL 3.0+) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation + glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) #endif - return fbo; -} + // Init state: Color/Depth buffers clear + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) + glClearDepth(1.0f); // Set clear depth value (default) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) -// Unload framebuffer object -void rlglUnloadFBO(FBO fbo) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteFramebuffers(1, &fbo.id); - glDeleteTextures(1, &fbo.colorTextureId); - glDeleteTextures(1, &fbo.depthTextureId); - - TraceLog(INFO, "[FBO ID %i] Unloaded framebuffer object successfully", fbo.id); -#endif + // Store screen size into global variables + screenWidth = width; + screenHeight = height; + + TraceLog(INFO, "OpenGL default states initialized successfully"); } // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Unbind everything - if (vaoSupported) glBindVertexArray(0); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(3); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glUseProgram(0); - - // Delete VBOs - glDeleteBuffers(1, &linesBuffer[0]); - glDeleteBuffers(1, &linesBuffer[1]); - glDeleteBuffers(1, &trianglesBuffer[0]); - glDeleteBuffers(1, &trianglesBuffer[1]); - glDeleteBuffers(1, &quadsBuffer[0]); - glDeleteBuffers(1, &quadsBuffer[1]); - glDeleteBuffers(1, &quadsBuffer[2]); - glDeleteBuffers(1, &quadsBuffer[3]); - - if (vaoSupported) - { - // Delete VAOs - glDeleteVertexArrays(1, &vaoLines); - glDeleteVertexArrays(1, &vaoTriangles); - glDeleteVertexArrays(1, &vaoQuads); - } - - //glDetachShader(defaultShaderProgram, v); - //glDetachShader(defaultShaderProgram, f); - //glDeleteShader(v); - //glDeleteShader(f); - glDeleteProgram(defaultShader.id); - glDeleteProgram(simpleShader.id); + UnloadDefaultShader(); + UnloadDefaultBuffers(); - // Free vertex arrays memory - free(lines.vertices); - free(lines.colors); - - free(triangles.vertices); - free(triangles.colors); - - free(quads.vertices); - free(quads.texcoords); - free(quads.colors); - free(quads.indices); - - // Free GPU texture + // Delete default white texture glDeleteTextures(1, &whiteTexture); TraceLog(INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); - if (postproFbo.id != 0) - { - rlglUnloadFBO(postproFbo); - - // Unload postpro quad model data -#if defined(GRAPHICS_API_OPENGL_11) - free(postproQuad.mesh.vertices); - free(postproQuad.mesh.texcoords); - free(postproQuad.mesh.normals); -#endif - - rlDeleteBuffers(postproQuad.mesh.vboId[0]); - rlDeleteBuffers(postproQuad.mesh.vboId[1]); - rlDeleteBuffers(postproQuad.mesh.vboId[2]); - - rlDeleteVertexArrays(postproQuad.mesh.vaoId); - - TraceLog(INFO, "Unloaded postprocessing data"); - } - free(draws); #endif } @@ -1242,424 +1347,63 @@ void rlglClose(void) void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UpdateBuffers(); - - if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) - { - glUseProgram(currentShader.id); - - Matrix matMVP = MatrixMultiply(modelview, projection); // Create modelview-projection matrix - - glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - glUniform1i(currentShader.mapDiffuseLoc, 0); - } - - // NOTE: We draw in this order: lines, triangles, quads - - if (lines.vCounter > 0) - { - glBindTexture(GL_TEXTURE_2D, whiteTexture); - - if (vaoSupported) - { - glBindVertexArray(vaoLines); - } - else - { - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); - - if (currentShader.colorLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); - } - } - - glDrawArrays(GL_LINES, 0, lines.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (triangles.vCounter > 0) - { - glBindTexture(GL_TEXTURE_2D, whiteTexture); - - if (vaoSupported) - { - glBindVertexArray(vaoTriangles); - } - else - { - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); - - if (currentShader.colorLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); - } - } - - glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (quads.vCounter > 0) - { - int quadsCount = 0; - int numIndicesToProcess = 0; - int indicesOffset = 0; - - if (vaoSupported) - { - glBindVertexArray(vaoQuads); - } - else - { - // Enable vertex attributes - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); - - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); - glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.texcoordLoc); - - if (currentShader.colorLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); - } - - //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter); - - for (int i = 0; i < drawsCounter; i++) - { - quadsCount = draws[i].vertexCount/4; - numIndicesToProcess = quadsCount*6; // Get number of Quads * 6 index by Quad - - //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); - - glBindTexture(GL_TEXTURE_2D, draws[i].textureId); - - // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process -#if defined(GRAPHICS_API_OPENGL_33) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset)); -#elif defined(GRAPHICS_API_OPENGL_ES2) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid*) (sizeof(GLushort) * indicesOffset)); -#endif - //GLenum err; - //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! - - indicesOffset += draws[i].vertexCount/4*6; - } - - if (!vaoSupported) - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - } - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO - - glUseProgram(0); // Unbind shader program - - // Reset draws counter - drawsCounter = 1; - draws[0].textureId = whiteTexture; - draws[0].vertexCount = 0; - - // Reset vertex counters for next frame - lines.vCounter = 0; - lines.cCounter = 0; - - triangles.vCounter = 0; - triangles.cCounter = 0; - - quads.vCounter = 0; - quads.tcCounter = 0; - quads.cCounter = 0; - - // Reset depth for next draw - currentDepth = -1.0f; -#endif -} - -// Draw with postprocessing shader -void rlglDrawPostpro(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindFramebuffer(GL_FRAMEBUFFER, 0); + // NOTE: In a future version, models could be stored in a stack... + //for (int i = 0; i < modelsCount; i++) rlglDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); - rlglDrawModel(postproQuad, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.0f, (Vector3){1.0f, 1.0f, 1.0f}, (Color){ 255, 255, 255, 255 }, false); -#endif -} + // NOTE: Default buffers upload and draw + UpdateDefaultBuffers(); -// Draw a 3d model -// NOTE: Model transform can come within model struct -void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color color, bool wires) -{ -#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); -#endif - -#if defined(GRAPHICS_API_OPENGL_11) - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, model.texture.id); - - // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model - 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.mesh.vertices); // Pointer to vertex coords array - glTexCoordPointer(2, GL_FLOAT, 0, model.mesh.texcoords); // Pointer to texture coords array - glNormalPointer(GL_FLOAT, 0, model.mesh.normals); // Pointer to normals array - //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.mesh.colors); // Pointer to colors array (NOT USED) - - rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - rlScalef(scale.x, scale.y, scale.z); - rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); - - rlColor4ub(color.r, color.g, color.b, color.a); - - glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); - rlPopMatrix(); - - glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array - glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array - glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array - - glDisable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); -#endif - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUseProgram(model.shader.id); - - // At this point the modelview matrix just contains the view matrix (camera) - // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() - Matrix matView = modelview; // View matrix (camera) - Matrix matProjection = projection; // Projection matrix (perspective) - - // Calculate transformation matrix from function parameters - // Get transform matrix (rotation -> scale -> translation) - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); - Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); - Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - - // Combine model internal transformation matrix (model.transform) with matrix generated by function parameters (matTransform) - Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates - - // Calculate model-view matrix combining matModel and matView - Matrix matModelView = MatrixMultiply(matModel, matView); // Transform to camera-space coordinates - - // Calculate model-view-projection matrix (MVP) - Matrix matMVP = MatrixMultiply(matModelView, matProjection); // Transform to screen-space coordinates - - // Send combined model-view-projection matrix to shader - glUniformMatrix4fv(model.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - - // Apply color tinting to model - // NOTE: Just update one uniform on fragment shader - float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 }; - glUniform4fv(model.shader.tintColorLoc, 1, vColor); - - // Set shader textures (diffuse, normal, specular) - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); - glUniform1i(model.shader.mapDiffuseLoc, 0); - - if (model.shader.texNormalId != 0) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, model.shader.texNormalId); - } - - if (model.shader.texSpecularId != 0) - { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, model.shader.texSpecularId); - } - - if (vaoSupported) - { - glBindVertexArray(model.mesh.vaoId); - } - else - { - // Bind model VBOs data - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[0]); - glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.vertexLoc); - - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[1]); - glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.texcoordLoc); - - // Add normals support - if (model.shader.normalLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[2]); - glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.normalLoc); - } - } - - // Draw call! - glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); - - //glDisableVertexAttribArray(model.shader.vertexLoc); - //glDisableVertexAttribArray(model.shader.texcoordLoc); - //if (model.shader.normalLoc != -1) glDisableVertexAttribArray(model.shader.normalLoc); - - if (model.shader.texNormalId != 0) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (model.shader.texSpecularId != 0) - { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO - else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs - - glUseProgram(0); // Unbind shader program -#endif - -#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + if (vrEnabled && vrRendering) DrawDefaultBuffers(2); + else DrawDefaultBuffers(1); #endif } -// Initialize Graphics Device (OpenGL stuff) -// NOTE: Stores global variables screenWidth and screenHeight -void rlglInitGraphics(int offsetX, int offsetY, int width, int height) +// Load OpenGL extensions +// NOTE: External loader function could be passed as a pointer +void rlglLoadExtensions(void *loader) { - // Save screen size data (global vars), required on postpro quad - // NOTE: Size represents render size, it could differ from screen size! - screenWidth = width; - screenHeight = height; - - // NOTE: Required! viewport must be recalculated if screen resized! - glViewport(offsetX/2, offsetY/2, width - offsetX, height - offsetY); // Set viewport width and height - - // NOTE: Don't confuse glViewport with the transformation matrix - // NOTE: glViewport just defines the area of the context that you will actually draw to. - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) - //glClearDepth(1.0f); // Clear depth buffer (default) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, depth buffer is used for 3D - - glEnable(GL_DEPTH_TEST); // Enables depth testing (required for 3D) - glDepthFunc(GL_LEQUAL); // Type of depth testing to apply - - glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) +#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) + #ifndef __APPLE__ + if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(WARNING, "GLAD: Cannot load OpenGL extensions"); + else TraceLog(INFO, "GLAD: OpenGL extensions loaded successfully"); + #endif -#if defined(GRAPHICS_API_OPENGL_11) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation (Deprecated in OGL 3.0) - // Other options: GL_FASTEST, GL_DONT_CARE (default) +#if defined(GRAPHICS_API_OPENGL_21) + #ifndef __APPLE__ + if (GLAD_GL_VERSION_2_1) TraceLog(INFO, "OpenGL 2.1 profile supported"); + #endif +#elif defined(GRAPHICS_API_OPENGL_33) + #ifndef __APPLE__ + if(GLAD_GL_VERSION_3_3) TraceLog(INFO, "OpenGL 3.3 Core profile supported"); + else TraceLog(ERROR, "OpenGL 3.3 Core profile not supported"); + #endif #endif - rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix - rlLoadIdentity(); // Reset current matrix (PROJECTION) - - rlOrtho(0, width - offsetX, height - offsetY, 0, 0.0f, 1.0f); // Config orthographic mode: top-left corner --> (0,0) - - rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix - rlLoadIdentity(); // Reset current matrix (MODELVIEW) - - // 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) - //glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) - -#if defined(GRAPHICS_API_OPENGL_11) - 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) + // With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans + //if (GLAD_GL_ARB_vertex_array_object) // Use GL_ARB_vertex_array_object #endif - - TraceLog(INFO, "OpenGL graphic device initialized successfully"); } // Get world coordinates from screen coordinates -// TODO: It doesn't work! It drives me crazy! -// NOTE: Using global variables: screenWidth, screenHeight Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) { - Vector3 result = { 0.0f, 0.0f, 0.0f }; // Object coordinates - - //GLint viewport[4]; - //glGetIntegerv(GL_VIEWPORT, viewport); // Not available on OpenGL ES 2.0 - - // Viewport data - int x = 0; // viewport[0] - int y = 0; // viewport[1] - int width = screenWidth; // viewport[2] - int height = screenHeight; // viewport[3] - - Matrix modelviewprojection = MatrixMultiply(view, proj); - MatrixInvert(&modelviewprojection); -/* - // NOTE: Compute unproject using Vector3 + Vector3 result = { 0.0f, 0.0f, 0.0f }; - // Transformation of normalized coordinates between -1 and 1 - result.x = ((source.x - (float)x)/(float)width)*2.0f - 1.0f; - result.y = ((source.y - (float)y)/(float)height)*2.0f - 1.0f; - result.z = source.z*2.0f - 1.0f; + // Calculate unproject matrix (multiply projection matrix and view matrix) and invert it + Matrix matProjView = MatrixMultiply(proj, view); + MatrixInvert(&matProjView); - // Object coordinates (multiply vector by matrix) - VectorTransform(&result, modelviewprojection); -*/ - - // NOTE: Compute unproject using Quaternion (Vector4) - Quaternion quat; - - quat.x = ((source.x - (float)x)/(float)width)*2.0f - 1.0f; - quat.y = ((source.y - (float)y)/(float)height)*2.0f - 1.0f; - quat.z = source.z*2.0f - 1.0f; - quat.w = 1.0f; - - QuaternionTransform(&quat, modelviewprojection); + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; - if (quat.w != 0.0f) - { - quat.x /= quat.w; - quat.y /= quat.w; - quat.z /= quat.w; - } + // Multiply quat point by unproject matrix + QuaternionTransform(&quat, matProjView); - result.x = quat.x; - result.y = quat.y; - result.z = quat.z; + // Normalized world points in vectors + result.x = quat.x/quat.w; + result.y = quat.y/quat.w; + result.z = quat.z/quat.w; return result; } @@ -1670,39 +1414,41 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding GLuint id = 0; - + // Check texture format support by OpenGL 1.1 (compressed textures not supported) - if ((rlGetVersion() == OPENGL_11) && (textureFormat >= 8)) +#if defined(GRAPHICS_API_OPENGL_11) + if (textureFormat >= 8) { TraceLog(WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); return id; } - +#endif + if ((!texCompDXTSupported) && ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT1_RGBA) || (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA))) { TraceLog(WARNING, "DXT compressed texture format not supported"); return id; } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((!texCompETC1Supported) && (textureFormat == COMPRESSED_ETC1_RGB)) { TraceLog(WARNING, "ETC1 compressed texture format not supported"); return id; } - + if ((!texCompETC2Supported) && ((textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA))) { TraceLog(WARNING, "ETC2 compressed texture format not supported"); return id; } - + if ((!texCompPVRTSupported) && ((textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA))) { TraceLog(WARNING, "PVRT compressed texture format not supported"); return id; } - + if ((!texCompASTCSupported) && ((textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA))) { TraceLog(WARNING, "ASTC compressed texture format not supported"); @@ -1730,24 +1476,24 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // GL_RGBA4 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4 // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE - + switch (textureFormat) { case UNCOMPRESSED_GRAYSCALE: { glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); - + // With swizzleMask we define how a one channel texture will be mapped to RGBA // Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - + TraceLog(INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", id); } break; case UNCOMPRESSED_GRAY_ALPHA: { glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); - + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } break; @@ -1821,7 +1567,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // Magnification and minification filters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR - + #if defined(GRAPHICS_API_OPENGL_33) if (mipmapCount > 1) { @@ -1831,7 +1577,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma #endif // At this point we have the texture loaded in GPU and texture parameters configured - + // NOTE: If mipmaps were not in data, they are not generated automatically // Unbind current texture @@ -1843,7 +1589,103 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma return id; } -void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data) +// Load a texture to be used for rendering (fbo with color and depth attachments) +RenderTexture2D rlglLoadRenderTexture(int width, int height) +{ + RenderTexture2D target; + + target.id = 0; + + target.texture.id = 0; + target.texture.width = width; + target.texture.height = height; + target.texture.format = UNCOMPRESSED_R8G8B8A8; + target.texture.mipmaps = 1; + + target.depth.id = 0; + target.depth.width = width; + target.depth.height = height; + target.depth.format = 19; //DEPTH_COMPONENT_24BIT + target.depth.mipmaps = 1; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Create the texture that will serve as the color attachment for the framebuffer + glGenTextures(1, &target.texture.id); + glBindTexture(GL_TEXTURE_2D, target.texture.id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + +#if defined(GRAPHICS_API_OPENGL_33) + #define USE_DEPTH_TEXTURE +#else + #define USE_DEPTH_RENDERBUFFER +#endif + +#if defined(USE_DEPTH_RENDERBUFFER) + // Create the renderbuffer that will serve as the depth attachment for the framebuffer. + glGenRenderbuffers(1, &target.depth.id); + glBindRenderbuffer(GL_RENDERBUFFER, target.depth.id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); // GL_DEPTH_COMPONENT24 not supported on Android +#elif defined(USE_DEPTH_TEXTURE) + // NOTE: We can also use a texture for depth buffer (GL_ARB_depth_texture/GL_OES_depth_texture extension required) + // A renderbuffer is simpler than a texture and could offer better performance on embedded devices + glGenTextures(1, &target.depth.id); + glBindTexture(GL_TEXTURE_2D, target.depth.id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + + // Create the framebuffer object + glGenFramebuffers(1, &target.id); + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + // Attach color texture and depth renderbuffer to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.texture.id, 0); +#if defined(USE_DEPTH_RENDERBUFFER) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, target.depth.id); +#elif defined(USE_DEPTH_TEXTURE) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target.depth.id, 0); +#endif + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + TraceLog(WARNING, "Framebuffer object could not be created..."); + + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(WARNING, "Framebuffer incomplete dimensions"); break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break; + default: break; + } + + glDeleteTextures(1, &target.texture.id); + glDeleteTextures(1, &target.depth.id); + glDeleteFramebuffers(1, &target.id); + } + else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return target; +} + +// Update already loaded texture in GPU with new data +void rlglUpdateTexture(unsigned int id, int width, int height, int format, const void *data) { glBindTexture(GL_TEXTURE_2D, id); @@ -1876,31 +1718,31 @@ void rlglUpdateTexture(unsigned int id, int width, int height, int format, void } // Generate mipmap data for selected texture -void rlglGenerateMipmaps(Texture2D texture) +void rlglGenerateMipmaps(Texture2D *texture) { - glBindTexture(GL_TEXTURE_2D, texture.id); - + glBindTexture(GL_TEXTURE_2D, texture->id); + // Check if texture is power-of-two (POT) bool texIsPOT = false; - - if (((texture.width > 0) && ((texture.width & (texture.width - 1)) == 0)) && - ((texture.height > 0) && ((texture.height & (texture.height - 1)) == 0))) texIsPOT = true; + + if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && + ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; if ((texIsPOT) || (npotSupported)) { #if defined(GRAPHICS_API_OPENGL_11) // Compute required mipmaps - void *data = rlglReadTexturePixels(texture); - + void *data = rlglReadTexturePixels(*texture); + // NOTE: data size is reallocated to fit mipmaps data - int mipmapCount = GenerateMipmaps(data, texture.width, texture.height); + // NOTE: CPU mipmap generation only supports RGBA 32bit data + int mipmapCount = GenerateMipmaps(data, texture->width, texture->height); - // TODO: Adjust mipmap size depending on texture format! - int size = texture.width*texture.height*4; // RGBA 32bit only + int size = texture->width*texture->height*4; // RGBA 32bit only int offset = size; - int mipWidth = texture.width/2; - int mipHeight = texture.height/2; + int mipWidth = texture->width/2; + int mipHeight = texture->height/2; // Load the mipmaps for (int level = 1; level < mipmapCount; level++) @@ -1913,102 +1755,476 @@ void rlglGenerateMipmaps(Texture2D texture) mipWidth /= 2; mipHeight /= 2; } - - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture.id); - -#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture->id); + + // NOTE: Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + free(data); + + texture->mipmaps = mipmapCount + 1; +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id); + TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture->id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps + + #define MIN(a,b) (((a)<(b))?(a):(b)) + #define MAX(a,b) (((a)>(b))?(a):(b)) + + texture->mipmaps = 1 + (int)floor(log(MAX(texture->width, texture->height))/log(2)); #endif } - else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture.id); - + else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id); + glBindTexture(GL_TEXTURE_2D, 0); } -// Load vertex data into a VAO (if supported) and VBO -Model rlglLoadModel(Mesh mesh) +// Upload vertex data into a VAO (if supported) and VBO +void rlglLoadMesh(Mesh *mesh, bool dynamic) { - Model model; + mesh->vaoId = 0; // Vertex Array Object + mesh->vboId[0] = 0; // Vertex positions VBO + mesh->vboId[1] = 0; // Vertex texcoords VBO + mesh->vboId[2] = 0; // Vertex normals VBO + mesh->vboId[3] = 0; // Vertex colors VBO + mesh->vboId[4] = 0; // Vertex tangents VBO + mesh->vboId[5] = 0; // Vertex texcoords2 VBO + mesh->vboId[6] = 0; // Vertex indices VBO - model.mesh = mesh; - model.transform = MatrixIdentity(); - model.mesh.vaoId = 0; // Vertex Array Object - model.mesh.vboId[0] = 0; // Vertex position VBO - model.mesh.vboId[1] = 0; // Texcoords VBO - model.mesh.vboId[2] = 0; // Normals VBO +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; -#if defined(GRAPHICS_API_OPENGL_11) - model.texture.id = 0; // No texture required - model.shader.id = 0; // No shader used - -#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - model.texture.id = whiteTexture; // Default whiteTexture - model.texture.width = 1; // Default whiteTexture width - model.texture.height = 1; // Default whiteTexture height - model.texture.format = UNCOMPRESSED_R8G8B8A8; // Default whiteTexture format - model.shader = simpleShader; // Default model shader - - GLuint vaoModel = 0; // Vertex Array Objects (VAO) - GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) + GLuint vaoId = 0; // Vertex Array Objects (VAO) + GLuint vboId[7] = { 0 }; // Vertex Buffer Objects (VBOs) if (vaoSupported) { // Initialize Quads VAO (Buffer A) - glGenVertexArrays(1, &vaoModel); - glBindVertexArray(vaoModel); + glGenVertexArrays(1, &vaoId); + glBindVertexArray(vaoId); } - // Create buffers for our vertex data (positions, texcoords, normals) - glGenBuffers(3, vertexBuffer); + // NOTE: Attributes must be uploaded considering default locations points - // Enable vertex attributes: position - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW); - glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.vertexLoc); + // Enable vertex attributes: position (shader-location = 0) + glGenBuffers(1, &vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(0); - // Enable vertex attributes: texcoords - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW); - glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.texcoordLoc); + // Enable vertex attributes: texcoords (shader-location = 1) + glGenBuffers(1, &vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(1); - // Enable vertex attributes: normals - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW); - glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.shader.normalLoc); + // Enable vertex attributes: normals (shader-location = 2) + if (mesh->normals != NULL) + { + glGenBuffers(1, &vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint); + glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(2); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(2); + } - model.mesh.vboId[0] = vertexBuffer[0]; // Vertex position VBO - model.mesh.vboId[1] = vertexBuffer[1]; // Texcoords VBO - model.mesh.vboId[2] = vertexBuffer[2]; // Normals VBO + // Default color vertex attribute (shader-location = 3) + if (mesh->colors != NULL) + { + glGenBuffers(1, &vboId[3]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[3]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(3); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(3); + } + + // Default tangent vertex attribute (shader-location = 4) + if (mesh->tangents != NULL) + { + glGenBuffers(1, &vboId[4]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[4]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->tangents, drawHint); + glVertexAttribPointer(4, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(4); + } + else + { + // Default tangents vertex attribute + glVertexAttrib3f(4, 0.0f, 0.0f, 0.0f); + glDisableVertexAttribArray(4); + } + + // Default texcoord2 vertex attribute (shader-location = 5) + if (mesh->texcoords2 != NULL) + { + glGenBuffers(1, &vboId[5]); + glBindBuffer(GL_ARRAY_BUFFER, vboId[5]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint); + glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(5); + } + else + { + // Default tangents vertex attribute + glVertexAttrib2f(5, 0.0f, 0.0f); + glDisableVertexAttribArray(5); + } + + if (mesh->indices != NULL) + { + glGenBuffers(1, &vboId[6]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[6]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*mesh->triangleCount*3, mesh->indices, GL_STATIC_DRAW); + } + + mesh->vboId[0] = vboId[0]; // Vertex position VBO + mesh->vboId[1] = vboId[1]; // Texcoords VBO + mesh->vboId[2] = vboId[2]; // Normals VBO + mesh->vboId[3] = vboId[3]; // Colors VBO + mesh->vboId[4] = vboId[4]; // Tangents VBO + mesh->vboId[5] = vboId[5]; // Texcoords2 VBO + mesh->vboId[6] = vboId[6]; // Indices VBO if (vaoSupported) { - if (vaoModel > 0) + if (vaoId > 0) { - model.mesh.vaoId = vaoModel; - TraceLog(INFO, "[VAO ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel); + mesh->vaoId = vaoId; + TraceLog(INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); } - else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)"); + else TraceLog(WARNING, "Mesh could not be uploaded to VRAM (GPU)"); } else { - TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Model uploaded successfully to VRAM (GPU)", model.mesh.vboId[0], model.mesh.vboId[1], model.mesh.vboId[2]); + TraceLog(INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); } #endif +} - return model; +// Update vertex data on GPU (upload new data to one buffer) +void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Activate mesh VAO + if (vaoSupported) glBindVertexArray(mesh.vaoId); + + switch (buffer) + { + case 0: // Update vertices (vertex position) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.vertices, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.vertices); + + } break; + case 1: // Update texcoords (vertex texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords); + + } break; + case 2: // Update normals (vertex normals) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.normals, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.normals); + + } break; + case 3: // Update colors (vertex colors) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.colors, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*numVertex, mesh.colors); + + } break; + case 4: // Update tangents (vertex tangents) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.tangents, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.tangents); + } break; + case 5: // Update texcoords2 (vertex second texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords2, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords2); + } break; + default: break; + } + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + + // Another option would be using buffer mapping... + //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); +#endif +} + +// Draw a 3d mesh with material and transform +void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + + // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array + + glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array + if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array + if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array + + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); + + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array + if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int eyesCount = 1; + if (vrEnabled) eyesCount = 2; + + glUseProgram(material.shader.id); + + // Upload to shader material.colDiffuse + glUniform4f(material.shader.colDiffuseLoc, (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255); + + // Upload to shader material.colAmbient (if available) + if (material.shader.colAmbientLoc != -1) glUniform4f(material.shader.colAmbientLoc, (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255); + + // Upload to shader material.colSpecular (if available) + if (material.shader.colSpecularLoc != -1) glUniform4f(material.shader.colSpecularLoc, (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); + + // At this point the modelview matrix just contains the view matrix (camera) + // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() + Matrix matView = modelview; // View matrix (camera) + Matrix matProjection = projection; // Projection matrix (perspective) + + // Calculate model-view matrix combining matModel and matView + Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates + + // If not using default shader, we check for some additional location points + // NOTE: This method is quite inefficient... it's a temporal solution while looking for a better one + if (material.shader.id != defaultShader.id) + { + // Check if model matrix is located in shader and upload value + int modelMatrixLoc = glGetUniformLocation(material.shader.id, "modelMatrix"); + if (modelMatrixLoc != -1) + { + // Transpose and inverse model transformations matrix for fragment normal calculations + Matrix transInvTransform = transform; + MatrixTranspose(&transInvTransform); + MatrixInvert(&transInvTransform); + + // Send model transformations matrix to shader + glUniformMatrix4fv(modelMatrixLoc, 1, false, MatrixToFloat(transInvTransform)); + } + + // Check if view direction is located in shader and upload value + // NOTE: View matrix values m8, m9 and m10 are view direction vector axis (target - position) + int viewDirLoc = glGetUniformLocation(material.shader.id, "viewDir"); + if (viewDirLoc != -1) glUniform3f(viewDirLoc, matView.m8, matView.m9, matView.m10); + + // Check if glossiness is located in shader and upload value + int glossinessLoc = glGetUniformLocation(material.shader.id, "glossiness"); + if (glossinessLoc != -1) glUniform1f(glossinessLoc, material.glossiness); + } + + // Set shader textures (diffuse, normal, specular) + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + glUniform1i(material.shader.mapTexture0Loc, 0); // Diffuse texture fits in active texture unit 0 + + if ((material.texNormal.id != 0) && (material.shader.mapTexture1Loc != -1)) + { + // Upload to shader specular map flag + glUniform1i(glGetUniformLocation(material.shader.id, "useNormal"), 1); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, material.texNormal.id); + glUniform1i(material.shader.mapTexture1Loc, 1); // Normal texture fits in active texture unit 1 + } + + if ((material.texSpecular.id != 0) && (material.shader.mapTexture2Loc != -1)) + { + // Upload to shader specular map flag + glUniform1i(glGetUniformLocation(material.shader.id, "useSpecular"), 1); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); + glUniform1i(material.shader.mapTexture2Loc, 2); // Specular texture fits in active texture unit 2 + } + + if (vaoSupported) + { + glBindVertexArray(mesh.vaoId); + } + else + { + // Bind mesh VBO data: vertex position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glVertexAttribPointer(material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.vertexLoc); + + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glVertexAttribPointer(material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.texcoordLoc); + + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.normalLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.normalLoc); + } + + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) + if (material.shader.colorLoc != -1) + { + if (mesh.vboId[3] != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.colorLoc); + } + else + { + // Set default value for unused attribute + // NOTE: Required when using default shader and no VAO support + glVertexAttrib4f(material.shader.colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(material.shader.colorLoc); + } + } + + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) + if (material.shader.tangentLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.tangentLoc); + } + + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) + if (material.shader.texcoord2Loc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.texcoord2Loc); + } + + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + } + + for (int eye = 0; eye < eyesCount; eye++) + { + if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); + else modelview = matModelView; + + // Calculate model-view-projection matrix (MVP) + Matrix matMVP = MatrixMultiply(modelview, projection); // Transform to screen-space coordinates + + // Send combined model-view-projection matrix to shader + glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + + // Draw call! + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + } + + if (material.texNormal.id != 0) + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (material.texSpecular.id != 0) + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + else + { + glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glUseProgram(0); // Unbind shader program + + // Restore projection/modelview matrices + projection = matProjection; + modelview = matView; +#endif +} + +// Unload mesh data from CPU and GPU +void rlglUnloadMesh(Mesh *mesh) +{ + if (mesh->vertices != NULL) free(mesh->vertices); + if (mesh->texcoords != NULL) free(mesh->texcoords); + if (mesh->normals != NULL) free(mesh->normals); + if (mesh->colors != NULL) free(mesh->colors); + if (mesh->tangents != NULL) free(mesh->tangents); + if (mesh->texcoords2 != NULL) free(mesh->texcoords2); + if (mesh->indices != NULL) free(mesh->indices); + + rlDeleteBuffers(mesh->vboId[0]); // vertex + rlDeleteBuffers(mesh->vboId[1]); // texcoords + rlDeleteBuffers(mesh->vboId[2]); // normals + rlDeleteBuffers(mesh->vboId[3]); // colors + rlDeleteBuffers(mesh->vboId[4]); // tangents + rlDeleteBuffers(mesh->vboId[5]); // texcoords2 + rlDeleteBuffers(mesh->vboId[6]); // indices + + rlDeleteVertexArrays(mesh->vaoId); } // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { - unsigned char *screenData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); + unsigned char *screenData = (unsigned char *)calloc(width*height*4, sizeof(unsigned char)); // NOTE: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); @@ -2022,7 +2238,7 @@ unsigned char *rlglReadScreenPixels(int width, int height) { // Flip line imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; - + // Set alpha component value to 255 (no trasparent image retrieval) // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; @@ -2040,10 +2256,10 @@ unsigned char *rlglReadScreenPixels(int width, int height) void *rlglReadTexturePixels(Texture2D texture) { void *pixels = NULL; - + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) glBindTexture(GL_TEXTURE_2D, texture.id); - + // NOTE: Using texture.id, we can retrieve some texture info (but not on OpenGL ES 2.0) /* int width, height, format; @@ -2052,11 +2268,11 @@ void *rlglReadTexturePixels(Texture2D texture) glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); // Other texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE */ - + int glFormat = 0, glType = 0; unsigned int size = texture.width*texture.height; - + // NOTE: GL_LUMINANCE and GL_LUMINANCE_ALPHA are removed since OpenGL 3.1 // Must be replaced by GL_RED and GL_RG on Core OpenGL 3.3 @@ -2065,8 +2281,8 @@ void *rlglReadTexturePixels(Texture2D texture) #if defined(GRAPHICS_API_OPENGL_11) case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_BYTE; break; // 8 bit per pixel (no alpha) case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_LUMINANCE_ALPHA; glType = GL_UNSIGNED_BYTE; break; // 16 bpp (2 channels) -#elif defined(GRAPHICS_API_OPENGL_33) - case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; break; +#elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; break; case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_RG; glType = GL_UNSIGNED_BYTE; break; #endif case UNCOMPRESSED_R5G6B5: pixels = (unsigned short *)malloc(size); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; break; // 16 bpp @@ -2076,25 +2292,26 @@ void *rlglReadTexturePixels(Texture2D texture) case UNCOMPRESSED_R8G8B8A8: pixels = (unsigned char *)malloc(size*4); glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; break; // 32 bpp default: TraceLog(WARNING, "Texture data retrieval, format not suported"); break; } - + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. - // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. - // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) glPixelStorei(GL_PACK_ALIGNMENT, 1); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); - + glBindTexture(GL_TEXTURE_2D, 0); #endif #if defined(GRAPHICS_API_OPENGL_ES2) - FBO fbo = rlglLoadFBO(texture.width, texture.height); - + + RenderTexture2D fbo = rlglLoadRenderTexture(texture.width, texture.height); + // NOTE: Two possible Options: // 1 - Bind texture to color fbo attachment and glReadPixels() // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() - + #define GET_TEXTURE_FBO_OPTION_1 // It works #if defined(GET_TEXTURE_FBO_OPTION_1) @@ -2104,228 +2321,164 @@ void *rlglReadTexturePixels(Texture2D texture) // Attach our texture to FBO -> Texture must be RGB // NOTE: Previoust attached texture is automatically detached glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); - + pixels = (unsigned char *)malloc(texture.width*texture.height*4*sizeof(unsigned char)); - + // NOTE: Despite FBO color texture is RGB, we read data as RGBA... reading as RGB doesn't work... o__O glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - + // Re-attach internal FBO color texture before deleting it - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.colorTextureId, 0); - + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.texture.id, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + #elif defined(GET_TEXTURE_FBO_OPTION_2) // Render texture to fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); - + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepthf(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); //glMatrixMode(GL_PROJECTION); //glLoadIdentity(); - rlOrtho(0.0, width, height, 0.0, 0.0, 1.0); + rlOrtho(0.0, width, height, 0.0, 0.0, 1.0); //glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); //glDisable(GL_TEXTURE_2D); //glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); - + Model quad; //quad.mesh = GenMeshQuad(width, height); quad.transform = MatrixIdentity(); - quad.shader = simpleShader; - + quad.shader = defaultShader; + DrawModel(quad, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, WHITE); - + pixels = (unsigned char *)malloc(texture.width*texture.height*3*sizeof(unsigned char)); - + glReadPixels(0, 0, texture.width, texture.height, GL_RGB, GL_UNSIGNED_BYTE, pixels); // Bind framebuffer 0, which means render to back buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); - + UnloadModel(quad); #endif // GET_TEXTURE_FBO_OPTION // Clean up temporal fbo - rlglUnloadFBO(fbo); + rlDeleteRenderTextures(fbo); + #endif return pixels; } +/* +// TODO: Record draw calls to be processed in batch +// NOTE: Global state must be kept +void rlglRecordDraw(void) +{ + // TODO: Before adding a new draw, check if anything changed from last stored draw +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + draws[drawsCounter].vaoId = currentState.vaoId; // lines.id, trangles.id, quads.id? + draws[drawsCounter].textureId = currentState.textureId; // whiteTexture? + draws[drawsCounter].shaderId = currentState.shaderId; // defaultShader.id + draws[drawsCounter].projection = projection; + draws[drawsCounter].modelview = modelview; + draws[drawsCounter].vertexCount = currentState.vertexCount; + + drawsCounter++; +#endif +} +*/ //---------------------------------------------------------------------------------- // Module Functions Definition - Shaders Functions // NOTE: Those functions are exposed directly to the user in raylib.h //---------------------------------------------------------------------------------- -// Load a custom shader and bind default locations -Shader LoadShader(char *vsFileName, char *fsFileName) +// Get default internal texture (white texture) +Texture2D GetDefaultTexture(void) { - Shader shader; - - shader.id = 0; // Default value in case of loading failure + Texture2D texture; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Shaders loading from external text file - char *vShaderStr = TextFileRead(vsFileName); - char *fShaderStr = TextFileRead(fsFileName); - - if ((vShaderStr != NULL) && (fShaderStr != NULL)) - { - shader.id = LoadShaderProgram(vShaderStr, fShaderStr); + texture.id = whiteTexture; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = UNCOMPRESSED_R8G8B8A8; - if (shader.id != 0) - { - TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shader.id); - - // Set shader textures ids (all 0 by default) - shader.texDiffuseId = 0; - shader.texNormalId = 0; - shader.texSpecularId = 0; - - // Get handles to GLSL input attibute locations - //------------------------------------------------------------------- - shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); - shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); - shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); - // NOTE: custom shader does not use colorLoc - shader.colorLoc = -1; - - // Get handles to GLSL uniform locations (vertex shader) - shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix"); - - // Get handles to GLSL uniform locations (fragment shader) - shader.tintColorLoc = glGetUniformLocation(shader.id, "fragTintColor"); - shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); - shader.mapNormalLoc = -1; // It can be set later - shader.mapSpecularLoc = -1; // It can be set later - //-------------------------------------------------------------------- - } - else - { - TraceLog(WARNING, "Custom shader could not be loaded"); - shader = simpleShader; - } - - // Shader strings must be freed - free(vShaderStr); - free(fShaderStr); - } - else - { - TraceLog(WARNING, "Custom shader could not be loaded"); - shader = simpleShader; - } -#endif - - return shader; + return texture; } -// Load custom shader strings and return program id -unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr) +// Load text data from file +// NOTE: text chars array should be freed manually +char *LoadText(const char *fileName) { - unsigned int program = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - GLuint vertexShader; - GLuint fragmentShader; - - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - const char *pvs = vShaderStr; - const char *pfs = fShaderStr; - - glShaderSource(vertexShader, 1, &pvs, NULL); - glShaderSource(fragmentShader, 1, &pfs, NULL); - - GLint success = 0; - - glCompileShader(vertexShader); + FILE *textFile; + char *text = NULL; - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + int count = 0; - if (success != GL_TRUE) + if (fileName != NULL) { - TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader); - - int maxLength = 0; - int length; - - glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); + textFile = fopen(fileName,"rt"); - char log[maxLength]; + if (textFile != NULL) + { + fseek(textFile, 0, SEEK_END); + count = ftell(textFile); + rewind(textFile); - glGetShaderInfoLog(vertexShader, maxLength, &length, log); + if (count > 0) + { + text = (char *)malloc(sizeof(char)*(count + 1)); + count = fread(text, sizeof(char), count, textFile); + text[count] = '\0'; + } - TraceLog(INFO, "%s", log); + fclose(textFile); + } + else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); } - else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); - - glCompileShader(fragmentShader); - - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - if (success != GL_TRUE) - { - TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader); + return text; +} - int maxLength = 0; - int length; +// Load shader from files and bind default locations +Shader LoadShader(char *vsFileName, char *fsFileName) +{ + Shader shader = { 0 }; - glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Shaders loading from external text file + char *vShaderStr = LoadText(vsFileName); + char *fShaderStr = LoadText(fsFileName); - char log[maxLength]; + if ((vShaderStr != NULL) && (fShaderStr != NULL)) + { + shader.id = LoadShaderProgram(vShaderStr, fShaderStr); - glGetShaderInfoLog(fragmentShader, maxLength, &length, log); + // After shader loading, we try to load default location names + if (shader.id != 0) LoadDefaultShaderLocations(&shader); - TraceLog(INFO, "%s", log); + // Shader strings must be freed + free(vShaderStr); + free(fShaderStr); } - else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); - program = glCreateProgram(); - - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - - glLinkProgram(program); - - // NOTE: All uniform variables are intitialised to 0 when a program links - - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) + if (shader.id == 0) { - TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program); - - int maxLength = 0; - int length; - - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); - - char log[maxLength]; - - glGetProgramInfoLog(program, maxLength, &length, log); - - TraceLog(INFO, "%s", log); - - glDeleteProgram(program); - - program = 0; + TraceLog(WARNING, "Custom shader could not be loaded"); + shader = defaultShader; } - else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); #endif - return program; + + return shader; } -// Unload a custom shader from memory +// Unload shader from GPU memory (VRAM) void UnloadShader(Shader shader) { if (shader.id != 0) @@ -2335,120 +2488,34 @@ void UnloadShader(Shader shader) } } -// Set custom shader to be used on batch draw -void SetCustomShader(Shader shader) +// Begin custom shader mode +void BeginShaderMode(Shader shader) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (currentShader.id != shader.id) { rlglDraw(); - currentShader = shader; -/* - if (vaoSupported) glBindVertexArray(vaoQuads); - - // Enable vertex attributes: position - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); - glEnableVertexAttribArray(currentShader.vertexLoc); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: texcoords - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); - glEnableVertexAttribArray(currentShader.texcoordLoc); - glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: colors - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); - glEnableVertexAttribArray(currentShader.colorLoc); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO -*/ - } -#endif -} - -// Set postprocessing shader -// NOTE: Uses global variables screenWidth and screenHeight -void SetPostproShader(Shader shader) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (!enabledPostpro) - { - enabledPostpro = true; - rlglInitPostpro(); } - - SetModelShader(&postproQuad, shader); - - Texture2D texture; - texture.id = postproFbo.colorTextureId; - texture.width = screenWidth; - texture.height = screenHeight; - - SetShaderMapDiffuse(&postproQuad.shader, texture); - - //TraceLog(DEBUG, "Postproquad texture id: %i", postproQuad.texture.id); - //TraceLog(DEBUG, "Postproquad shader diffuse map id: %i", postproQuad.shader.texDiffuseId); - //TraceLog(DEBUG, "Shader diffuse map id: %i", shader.texDiffuseId); -#elif defined(GRAPHICS_API_OPENGL_11) - TraceLog(WARNING, "Shaders not supported on OpenGL 1.1"); #endif } -// Set default shader to be used in batch draw -void SetDefaultShader(void) +// End custom shader mode (returns to default shader) +void EndShaderMode(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - SetCustomShader(defaultShader); - - if (enabledPostpro) - { - SetPostproShader(defaultShader); - enabledPostpro = false; - } + BeginShaderMode(defaultShader); #endif } -// Link shader to model -void SetModelShader(Model *model, Shader shader) +// Get default shader +Shader GetDefaultShader(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - model->shader = shader; - - if (vaoSupported) glBindVertexArray(model->mesh.vaoId); - - // Enable vertex attributes: position - glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[0]); - glEnableVertexAttribArray(shader.vertexLoc); - glVertexAttribPointer(shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: texcoords - glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[1]); - glEnableVertexAttribArray(shader.texcoordLoc); - glVertexAttribPointer(shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - - // Enable vertex attributes: normals - glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[2]); - glEnableVertexAttribArray(shader.normalLoc); - glVertexAttribPointer(shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO - - // NOTE: If SetModelTexture() is called previously, texture is not assigned to new shader - if (model->texture.id > 0) model->shader.texDiffuseId = model->texture.id; -#elif (GRAPHICS_API_OPENGL_11) - TraceLog(WARNING, "Shaders not supported on OpenGL 1.1"); -#endif -} - -// Check if postprocessing is enabled (used in module: core) -bool IsPosproShaderEnabled(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - return enabledPostpro; -#elif defined(GRAPHICS_API_OPENGL_11) - return false; + return defaultShader; +#else + Shader shader = { 0 }; + return shader; #endif } @@ -2456,10 +2523,10 @@ bool IsPosproShaderEnabled(void) int GetShaderLocation(Shader shader, const char *uniformName) { int location = -1; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetUniformLocation(shader.id, uniformName); - - if (location == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader.id, uniformName); + + if (location == -1) TraceLog(DEBUG, "[SHDR ID %i] Shader location for %s could not be found", shader.id, uniformName); #endif return location; } @@ -2475,8 +2542,8 @@ void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) else if (size == 3) glUniform3fv(uniformLoc, 1, value); // Shader uniform type: vec3 else if (size == 4) glUniform4fv(uniformLoc, 1, value); // Shader uniform type: vec4 else TraceLog(WARNING, "Shader value float array size not supported"); - - glUseProgram(0); + + //glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set #endif } @@ -2491,8 +2558,8 @@ void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size) else if (size == 3) glUniform3iv(uniformLoc, 1, value); // Shader uniform type: ivec3 else if (size == 4) glUniform4iv(uniformLoc, 1, value); // Shader uniform type: ivec4 else TraceLog(WARNING, "Shader value int array size not supported"); - - glUseProgram(0); + + //glUseProgram(0); #endif } @@ -2503,140 +2570,302 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) glUseProgram(shader.id); glUniformMatrix4fv(uniformLoc, 1, false, MatrixToFloat(mat)); - - glUseProgram(0); + + //glUseProgram(0); #endif } -// Default diffuse shader map texture assignment -void SetShaderMapDiffuse(Shader *shader, Texture2D texture) +// Set a custom projection matrix (replaces internal projection matrix) +void SetMatrixProjection(Matrix proj) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - shader->texDiffuseId = texture.id; - - glUseProgram(shader->id); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, shader->texDiffuseId); - - glUniform1i(shader->mapDiffuseLoc, 0); // Texture fits in active texture unit 0 - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + projection = proj; #endif } -// Normal map texture shader assignment -void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture) +// Set a custom modelview matrix (replaces internal modelview matrix) +void SetMatrixModelview(Matrix view) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - shader->mapNormalLoc = glGetUniformLocation(shader->id, uniformName); + modelview = view; +#endif +} - if (shader->mapNormalLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); - else +// Begin blending mode (alpha, additive, multiplied) +// NOTE: Only 3 blending modes supported, default blend mode is alpha +void BeginBlendMode(int mode) +{ + if ((blendMode != mode) && (mode < 3)) { - shader->texNormalId = texture.id; - - glUseProgram(shader->id); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, shader->texNormalId); - - glUniform1i(shader->mapNormalLoc, 1); // Texture fits in active texture unit 1 - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + rlglDraw(); + + switch (mode) + { + case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; // Alternative: glBlendFunc(GL_ONE, GL_ONE); + case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; + default: break; + } + + blendMode = mode; } -#endif } -// Specular map texture shader assignment -void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture) +// End blending mode (reset to default: alpha blending) +void EndBlendMode(void) +{ + BeginBlendMode(BLEND_ALPHA); +} + +// Init VR device (or simulator) +// NOTE: If device is not available, it fallbacks to default device (simulator) +// NOTE: It modifies the global variable: VrDeviceInfo hmd +void InitVrDevice(int vrDevice) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - shader->mapSpecularLoc = glGetUniformLocation(shader->id, uniformName); + switch (vrDevice) + { + case HMD_DEFAULT_DEVICE: TraceLog(INFO, "Initializing default VR Device (Oculus Rift CV1)"); + case HMD_OCULUS_RIFT_DK2: + case HMD_OCULUS_RIFT_CV1: + { +#if defined(RLGL_OCULUS_SUPPORT) + vrDeviceReady = InitOculusDevice(); +#else + TraceLog(WARNING, "Oculus Rift not supported by default, recompile raylib with Oculus support"); +#endif + } break; + case HMD_VALVE_HTC_VIVE: + case HMD_SAMSUNG_GEAR_VR: + case HMD_GOOGLE_CARDBOARD: + case HMD_SONY_PLAYSTATION_VR: + case HMD_RAZER_OSVR: + case HMD_FOVE_VR: TraceLog(WARNING, "VR Device not supported"); + default: break; + } - if (shader->mapSpecularLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); - else + if (!vrDeviceReady) { - shader->texSpecularId = texture.id; - - glUseProgram(shader->id); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, shader->texSpecularId); - - glUniform1i(shader->mapSpecularLoc, 2); // Texture fits in active texture unit 2 - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + TraceLog(WARNING, "VR Device not found: Initializing VR Simulator (Oculus Rift CV1)"); + + if (vrDevice == HMD_OCULUS_RIFT_DK2) + { + // Oculus Rift DK2 parameters + hmd.hResolution = 1280; // HMD horizontal resolution in pixels + hmd.vResolution = 800; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.14976f; // HMD horizontal size in meters + hmd.vScreenSize = 0.09356f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters + hmd.distortionK[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.distortionK[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.distortionK[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.distortionK[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + } + else if ((vrDevice == HMD_DEFAULT_DEVICE) || (vrDevice == HMD_OCULUS_RIFT_CV1)) + { + // Oculus Rift CV1 parameters + // NOTE: CV1 represents a complete HMD redesign compared to previous versions, + // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, + // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. + // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering + // but result is not the same obtained with Oculus PC SDK. + hmd.hResolution = 2160; // HMD horizontal resolution in pixels + hmd.vResolution = 1200; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters + hmd.vScreenSize = 0.0669f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters + hmd.distortionK[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.distortionK[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.distortionK[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.distortionK[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + } + + // Initialize framebuffer and textures for stereo rendering + // NOTE: screen size should match HMD aspect ratio + vrConfig.stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight); + + // Load distortion shader (initialized by default with Oculus Rift CV1 parameters) + vrConfig.distortionShader.id = LoadShaderProgram(vDistortionShaderStr, fDistortionShaderStr); + if (vrConfig.distortionShader.id != 0) LoadDefaultShaderLocations(&vrConfig.distortionShader); + + SetStereoConfig(hmd); + + vrSimulator = true; + vrEnabled = true; } #endif + +#if defined(GRAPHICS_API_OPENGL_11) + TraceLog(WARNING, "VR device or simulator not supported on OpenGL 1.1"); +#endif } -// Generic shader maps assignment -// TODO: Trying to find a generic shader to allow any kind of map -// NOTE: mapLocation should be retrieved by user with GetShaderLocation() -// ISSUE: mapTextureId: Shader should contain a reference to map texture and corresponding textureUnit, -// so it can be automatically checked and used in rlglDrawModel() -void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textureUnit) +// Close VR device (or simulator) +void CloseVrDevice(void) { -/* #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (mapLocation == -1) TraceLog(WARNING, "[SHDR ID %i] Map location could not be found", shader->id); +#if defined(RLGL_OCULUS_SUPPORT) + if (vrDeviceReady) CloseOculusDevice(); else +#endif { - shader->mapTextureId = texture.id; - - glUseProgram(shader->id); - - glActiveTexture(GL_TEXTURE0 + textureUnit); - glBindTexture(GL_TEXTURE_2D, shader->mapTextureId); - - glUniform1i(mapLocation, textureUnit); // Texture fits in active textureUnit - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture + UnloadShader(vrConfig.distortionShader); // Unload distortion shader } #endif -*/ + vrDeviceReady = false; } -// Set blending mode (alpha, additive, multiplied) -// NOTE: Only 3 blending modes predefined -void SetBlendMode(int mode) +// Detect if VR device is available +bool IsVrDeviceReady(void) { - if ((blendMode != mode) && (mode < 3)) + return (vrDeviceReady && vrEnabled); +} + +// Detect if VR simulator is running +bool IsVrSimulator(void) +{ + return (vrSimulator && vrEnabled); +} + +// Enable/Disable VR experience (device or simulator) +void ToggleVrMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrDeviceReady || vrSimulator) vrEnabled = !vrEnabled; + else vrEnabled = false; + + if (!vrEnabled) { - rlglDraw(); - - switch (mode) - { - case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; - case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; // Alternative: glBlendFunc(GL_ONE, GL_ONE); - case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; - default: break; - } - - blendMode = mode; + // Reset viewport and default projection-modelview matrices + rlViewport(0, 0, screenWidth, screenHeight); + projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f); + MatrixTranspose(&projection); + modelview = MatrixIdentity(); } +#endif } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void PrintProjectionMatrix(void) +// Update VR tracking (position and orientation) and camera +// NOTE: Camera (position, target, up) gets update with head tracking information +void UpdateVrTracking(Camera *camera) { - PrintMatrix(projection); +#if defined(RLGL_OCULUS_SUPPORT) + if (vrDeviceReady) UpdateOculusTracking(camera); +#endif } -void PrintModelviewMatrix(void) +// Begin Oculus drawing configuration +void BeginVrDrawing(void) { - PrintMatrix(modelview); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(RLGL_OCULUS_SUPPORT) + if (vrDeviceReady) + { + BeginOculusDrawing(); + } + else +#endif + { + // Setup framebuffer for stereo rendering + rlEnableRenderTexture(vrConfig.stereoFbo.id); + } + + // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) + // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: + // - Require OculusBuffer format to be OVR_FORMAT_R8G8B8A8_UNORM_SRGB + // - Do NOT enable GL_FRAMEBUFFER_SRGB + //glEnable(GL_FRAMEBUFFER_SRGB); + + //glViewport(0, 0, buffer.width, buffer.height); // Useful if rendering to separate framebuffers (every eye) + rlClearScreenBuffers(); // Clear current framebuffer(s) + + vrRendering = true; +#endif } + +// End Oculus drawing process (and desktop mirror) +void EndVrDrawing(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(RLGL_OCULUS_SUPPORT) + if (vrDeviceReady) + { + EndOculusDrawing(); + } + else +#endif + { + // Unbind current framebuffer + rlDisableRenderTexture(); + + rlClearScreenBuffers(); // Clear current framebuffer + + // Set viewport to default framebuffer size (screen size) + rlViewport(0, 0, screenWidth, screenHeight); + + // Let rlgl reconfigure internal matrices + rlMatrixMode(RL_PROJECTION); // Enable internal projection matrix + rlLoadIdentity(); // Reset internal projection matrix + rlOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0); // Recalculate internal projection matrix + rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix + rlLoadIdentity(); // Reset internal modelview matrix + + // Draw RenderTexture (stereoFbo) using distortion shader + currentShader = vrConfig.distortionShader; + + rlEnableTexture(vrConfig.stereoFbo.texture.id); + + rlPushMatrix(); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlNormal3f(0.0f, 0.0f, 1.0f); + + // Bottom-left corner for texture and quad + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(0.0f, vrConfig.stereoFbo.texture.height); + + // Top-right corner for texture and quad + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f(vrConfig.stereoFbo.texture.width, vrConfig.stereoFbo.texture.height); + + // Top-left corner for texture and quad + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f(vrConfig.stereoFbo.texture.width, 0.0f); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + UpdateDefaultBuffers(); + DrawDefaultBuffers(1); + + currentShader = defaultShader; + } + + rlDisableDepthTest(); + + vrRendering = false; #endif +} //---------------------------------------------------------------------------------- // Module specific Functions Definition @@ -2664,7 +2893,7 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in for (int level = 0; level < mipmapCount && (width || height); level++) { unsigned int size = 0; - + size = ((width + 3)/4)*((height + 3)/4)*blockSize; glCompressedTexImage2D(GL_TEXTURE_2D, level, compressedFormat, width, height, 0, size, data + offset); @@ -2679,224 +2908,292 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in } } -// Load Shader (Vertex and Fragment) -// NOTE: This shader program is used only for batch buffers (lines, triangles, quads) -static Shader LoadDefaultShader(void) +// Load custom shader strings and return program id +static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShaderStr) { - Shader shader; + unsigned int program = 0; - // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 - // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ - // Just defined #version 330 despite shader is #version 110 +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + GLuint vertexShader; + GLuint fragmentShader; - // Vertex shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = "#version 330 \n" - "in vec3 vertexPosition; \n" - "in vec2 vertexTexCoord; \n" - "in vec4 vertexColor; \n" - "out vec2 fragTexCoord; \n" - "out vec4 fragTintColor; \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) - char vShaderStr[] = "#version 100 \n" - "attribute vec3 vertexPosition; \n" - "attribute vec2 vertexTexCoord; \n" - "attribute vec4 vertexColor; \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragTintColor; \n" -#endif - "uniform mat4 mvpMatrix; \n" - "void main() \n" - "{ \n" - " fragTexCoord = vertexTexCoord; \n" - " fragTintColor = vertexColor; \n" - " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" - "} \n"; + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - // Fragment shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragTintColor; \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) - char fShaderStr[] = "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragTintColor; \n" + const char *pvs = vShaderStr; + const char *pfs = fShaderStr; + + glShaderSource(vertexShader, 1, &pvs, NULL); + glShaderSource(fragmentShader, 1, &pfs, NULL); + + GLint success = 0; + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) + { + TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader); + + int maxLength = 0; + int length; + + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); + +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else + char log[maxLength]; #endif - "uniform sampler2D texture0; \n" - "void main() \n" - "{ \n" - " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead - " gl_FragColor = texelColor*fragTintColor; \n" - "} \n"; + glGetShaderInfoLog(vertexShader, maxLength, &length, log); - shader.id = LoadShaderProgram(vShaderStr, fShaderStr); + TraceLog(INFO, "%s", log); - if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); - else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); +#ifdef _MSC_VER + free(log); +#endif + } + else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); - // Get handles to GLSL input attibute locations - //------------------------------------------------------------------- - shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); - shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); - shader.colorLoc = glGetAttribLocation(shader.id, "vertexColor"); - // NOTE: default shader does not use normalLoc - shader.normalLoc = -1; + glCompileShader(fragmentShader); - // Get handles to GLSL uniform locations (vertex shader) - shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix"); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - // Get handles to GLSL uniform locations (fragment shader) - shader.tintColorLoc = -1; - shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); - shader.mapNormalLoc = -1; // It can be set later - shader.mapSpecularLoc = -1; // It can be set later - - shader.texDiffuseId = whiteTexture; // Default white texture - shader.texNormalId = 0; - shader.texSpecularId = 0; - //-------------------------------------------------------------------- + if (success != GL_TRUE) + { + TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader); - return shader; + int maxLength = 0; + int length; + + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); + +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else + char log[maxLength]; +#endif + glGetShaderInfoLog(fragmentShader, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + +#ifdef _MSC_VER + free(log); +#endif + } + else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); + + program = glCreateProgram(); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + // NOTE: Default attribute shader locations must be binded before linking + glBindAttribLocation(program, 0, DEFAULT_ATTRIB_POSITION_NAME); + glBindAttribLocation(program, 1, DEFAULT_ATTRIB_TEXCOORD_NAME); + glBindAttribLocation(program, 2, DEFAULT_ATTRIB_NORMAL_NAME); + glBindAttribLocation(program, 3, DEFAULT_ATTRIB_COLOR_NAME); + glBindAttribLocation(program, 4, DEFAULT_ATTRIB_TANGENT_NAME); + glBindAttribLocation(program, 5, DEFAULT_ATTRIB_TEXCOORD2_NAME); + + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 + + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program); + + int maxLength = 0; + int length; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else + char log[maxLength]; +#endif + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + +#ifdef _MSC_VER + free(log); +#endif + glDeleteProgram(program); + + program = 0; + } + else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); +#endif + return program; } -// Load Simple Shader (Vertex and Fragment) -// NOTE: This shader program is used to render models -static Shader LoadSimpleShader(void) + +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for batch buffers (lines, triangles, quads) +static Shader LoadDefaultShader(void) { Shader shader; - // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 - // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ - // Just defined #version 330 despite shader is #version 110 - // Vertex shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = "#version 330 \n" - "in vec3 vertexPosition; \n" - "in vec2 vertexTexCoord; \n" - "in vec3 vertexNormal; \n" - "out vec2 fragTexCoord; \n" + char vDefaultShaderStr[] = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char vShaderStr[] = "#version 100 \n" - "attribute vec3 vertexPosition; \n" - "attribute vec2 vertexTexCoord; \n" - "attribute vec3 vertexNormal; \n" - "varying vec2 fragTexCoord; \n" -#endif - "uniform mat4 mvpMatrix; \n" - "void main() \n" - "{ \n" - " fragTexCoord = vertexTexCoord; \n" - " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" - "} \n"; + "#version 100 \n" +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" +#endif + "uniform mat4 mvpMatrix; \n" + "void main() \n" + "{ \n" + " fragTexCoord = vertexTexCoord; \n" + " fragColor = vertexColor; \n" + " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" + "} \n"; // Fragment shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = "#version 330 \n" - "in vec2 fragTexCoord; \n" + char fDefaultShaderStr[] = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char fShaderStr[] = "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - "varying vec2 fragTexCoord; \n" + "#version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) #endif - "uniform sampler2D texture0; \n" - "uniform vec4 fragTintColor; \n" - "void main() \n" - "{ \n" - " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead - " gl_FragColor = texelColor*fragTintColor; \n" - "} \n"; - - shader.id = LoadShaderProgram(vShaderStr, fShaderStr); - - if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id); - else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id); +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" +#endif + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" +#endif + "} \n"; - // Get handles to GLSL input attibute locations - //------------------------------------------------------------------- - shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); - shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); - shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); - // NOTE: simple shader does not use colorLoc - shader.colorLoc = -1; + shader.id = LoadShaderProgram(vDefaultShaderStr, fDefaultShaderStr); - // Get handles to GLSL uniform locations (vertex shader) - shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix"); + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); - // Get handles to GLSL uniform locations (fragment shader) - shader.tintColorLoc = glGetUniformLocation(shader.id, "fragTintColor"); - shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); - shader.mapNormalLoc = -1; // It can be set later - shader.mapSpecularLoc = -1; // It can be set later - - shader.texDiffuseId = whiteTexture; // Default white texture - shader.texNormalId = 0; - shader.texSpecularId = 0; - //-------------------------------------------------------------------- + if (shader.id != 0) LoadDefaultShaderLocations(&shader); return shader; } -// Read text file -// NOTE: text chars array should be freed manually -static char *TextFileRead(char *fileName) +// Get location handlers to for shader attributes and uniforms +// NOTE: If any location is not found, loc point becomes -1 +static void LoadDefaultShaderLocations(Shader *shader) { - FILE *textFile; - char *text = NULL; + // NOTE: Default shader attrib locations have been fixed before linking: + // vertex position location = 0 + // vertex texcoord location = 1 + // vertex normal location = 2 + // vertex color location = 3 + // vertex tangent location = 4 + // vertex texcoord2 location = 5 - int count = 0; + // Get handles to GLSL input attibute locations + shader->vertexLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); + shader->texcoordLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); + shader->texcoord2Loc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); + shader->normalLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); + shader->tangentLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); + shader->colorLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); - if (fileName != NULL) - { - textFile = fopen(fileName,"rt"); + // Get handles to GLSL uniform locations (vertex shader) + shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix"); - if (textFile != NULL) - { - fseek(textFile, 0, SEEK_END); - count = ftell(textFile); - rewind(textFile); + // Get handles to GLSL uniform locations (fragment shader) + shader->colDiffuseLoc = glGetUniformLocation(shader->id, "colDiffuse"); + shader->colAmbientLoc = glGetUniformLocation(shader->id, "colAmbient"); + shader->colSpecularLoc = glGetUniformLocation(shader->id, "colSpecular"); - if (count > 0) - { - text = (char *)malloc(sizeof(char)*(count + 1)); - count = fread(text, sizeof(char), count, textFile); - text[count] = '\0'; - } + shader->mapTexture0Loc = glGetUniformLocation(shader->id, "texture0"); + shader->mapTexture1Loc = glGetUniformLocation(shader->id, "texture1"); + shader->mapTexture2Loc = glGetUniformLocation(shader->id, "texture2"); - fclose(textFile); - } - else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); - } + // TODO: Try to find all expected/recognized shader locations (predefined names, must be documented) +} - return text; +// Unload default shader +static void UnloadDefaultShader(void) +{ + glUseProgram(0); + + //glDetachShader(defaultShader, vertexShader); + //glDetachShader(defaultShader, fragmentShader); + //glDeleteShader(vertexShader); // Already deleted on shader compilation + //glDeleteShader(fragmentShader); // Already deleted on shader compilation + glDeleteProgram(defaultShader.id); } -// Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) -static void InitializeBuffers(void) +// Load default internal buffers (lines, triangles, quads) +static void LoadDefaultBuffers(void) { - // Initialize lines arrays (vertex position and color data) + // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) + //-------------------------------------------------------------------------------------------- + + // Lines - Initialize arrays (vertex position and color data) lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line + lines.texcoords = NULL; + lines.indices = NULL; for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f; for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0; lines.vCounter = 0; lines.cCounter = 0; + lines.tcCounter = 0; - // Initialize triangles arrays (vertex position and color data) + // Triangles - Initialize arrays (vertex position and color data) triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle + triangles.texcoords = NULL; + triangles.indices = NULL; for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f; for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0; triangles.vCounter = 0; triangles.cCounter = 0; + triangles.tcCounter = 0; - // Initialize quads arrays (vertex position, texcoord and color data... and indexes) + // Quads - Initialize arrays (vertex position, texcoord, color data and indexes) quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad @@ -2906,7 +3203,6 @@ static void InitializeBuffers(void) quads.indices = (unsigned short *)malloc(sizeof(short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) #endif - for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f; for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f; for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0; @@ -2930,166 +3226,174 @@ static void InitializeBuffers(void) quads.tcCounter = 0; quads.cCounter = 0; - TraceLog(INFO, "CPU buffers (lines, triangles, quads) initialized successfully"); -} + TraceLog(INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); + //-------------------------------------------------------------------------------------------- -// Initialize Vertex Array Objects (Contain VBO) -// NOTE: lines, triangles and quads buffers use currentShader -static void InitializeBuffersGPU(void) -{ + // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) + // NOTE: Default buffers are linked to use currentShader (defaultShader) + //-------------------------------------------------------------------------------------------- + + // Upload and link lines vertex buffers if (vaoSupported) { // Initialize Lines VAO - glGenVertexArrays(1, &vaoLines); - glBindVertexArray(vaoLines); + glGenVertexArrays(1, &lines.vaoId); + glBindVertexArray(lines.vaoId); } - // Create buffers for our vertex data - glGenBuffers(2, linesBuffer); - - // Lines - Vertex positions buffer binding and attributes enable - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); + // Lines - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(2, &lines.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.vertexLoc); glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - // Lines - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); + // Vertex color buffer (shader-location = 3) + glGenBuffers(2, &lines.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.colorLoc); glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]); - //-------------------------------------------------------------- + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); + // Upload and link triangles vertex buffers if (vaoSupported) { // Initialize Triangles VAO - glGenVertexArrays(1, &vaoTriangles); - glBindVertexArray(vaoTriangles); + glGenVertexArrays(1, &triangles.vaoId); + glBindVertexArray(triangles.vaoId); } - // Create buffers for our vertex data - glGenBuffers(2, trianglesBuffer); - - // Enable vertex attributes - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); + // Triangles - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &triangles.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.vertexLoc); glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &triangles.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.colorLoc); glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]); - //-------------------------------------------------------------- + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); + // Upload and link quads vertex buffers if (vaoSupported) { // Initialize Quads VAO - glGenVertexArrays(1, &vaoQuads); - glBindVertexArray(vaoQuads); + glGenVertexArrays(1, &quads.vaoId); + glBindVertexArray(quads.vaoId); } - // Create buffers for our vertex data - glGenBuffers(4, quadsBuffer); - - // Enable vertex attributes - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &quads.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.vertexLoc); glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &quads.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.texcoordLoc); glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &quads.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.colorLoc); glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); // Fill index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); + glGenBuffers(1, &quads.vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); #if defined(GRAPHICS_API_OPENGL_33) glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); #elif defined(GRAPHICS_API_OPENGL_ES2) glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); #endif - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Quads VAO initialized successfully", vaoQuads); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Quads VBOs initialized successfully", quadsBuffer[0], quadsBuffer[1], quadsBuffer[2], quadsBuffer[3]); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- } -// Update VBOs with vertex array data +// Update default internal buffers (VAOs/VBOs) with vertex array data // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) -// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays (change flag required) -static void UpdateBuffers(void) +// TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) +static void UpdateDefaultBuffers(void) { + // Update lines vertex buffers if (lines.vCounter > 0) { // Activate Lines VAO - if (vaoSupported) glBindVertexArray(vaoLines); + if (vaoSupported) glBindVertexArray(lines.vaoId); // Lines - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*lines.vCounter, lines.vertices); // target - offset (in bytes) - size (in bytes) - data pointer // Lines - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*lines.cCounter, lines.colors); } - //-------------------------------------------------------------- + // Update triangles vertex buffers if (triangles.vCounter > 0) { // Activate Triangles VAO - if (vaoSupported) glBindVertexArray(vaoTriangles); + if (vaoSupported) glBindVertexArray(triangles.vaoId); // Triangles - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); //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]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*triangles.cCounter, triangles.colors); } - //-------------------------------------------------------------- + // Update quads vertex buffers if (quads.vCounter > 0) { // Activate Quads VAO - if (vaoSupported) glBindVertexArray(vaoQuads); + if (vaoSupported) glBindVertexArray(quads.vaoId); // Quads - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*quads.vCounter, quads.vertices); // Quads - texture coordinates buffer - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*quads.vCounter, quads.texcoords); // Quads - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*quads.vCounter, quads.colors); // Another option would be using buffer mapping... - //triangles.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + //quads.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); // Now we can modify vertices //glUnmapBuffer(GL_ARRAY_BUFFER); } @@ -3098,6 +3402,339 @@ static void UpdateBuffers(void) // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); } + +// Draw default internal buffers vertex data +// NOTE: We draw in this order: lines, triangles, quads +static void DrawDefaultBuffers(int eyesCount) +{ + Matrix matProjection = projection; + Matrix matModelView = modelview; + + for (int eye = 0; eye < eyesCount; eye++) + { + if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); + + // Set current shader and upload current MVP matrix + if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) + { + glUseProgram(currentShader.id); + + // Create modelview-projection matrix + Matrix matMVP = MatrixMultiply(modelview, projection); + + glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + glUniform4f(currentShader.colDiffuseLoc, 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(currentShader.mapTexture0Loc, 0); + + // NOTE: Additional map textures not considered for default buffers drawing + } + + // Draw lines buffers + if (lines.vCounter > 0) + { + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(lines.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + } + + glDrawArrays(GL_LINES, 0, lines.vCounter); + + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Draw triangles buffers + if (triangles.vCounter > 0) + { + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(triangles.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + } + + glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); + + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Draw quads buffers + if (quads.vCounter > 0) + { + int quadsCount = 0; + int numIndicesToProcess = 0; + int indicesOffset = 0; + + if (vaoSupported) + { + glBindVertexArray(quads.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.texcoordLoc); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + } + + //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter); + + for (int i = 0; i < drawsCounter; i++) + { + quadsCount = draws[i].vertexCount/4; + numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad + + //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); + + glBindTexture(GL_TEXTURE_2D, draws[i].textureId); + + // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process + #if defined(GRAPHICS_API_OPENGL_33) + glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*indicesOffset)); + #elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset)); + #endif + //GLenum err; + //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! + + indicesOffset += draws[i].vertexCount/4*6; + } + + if (!vaoSupported) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + + glUseProgram(0); // Unbind shader program + } + + // Reset draws counter + drawsCounter = 1; + draws[0].textureId = whiteTexture; + draws[0].vertexCount = 0; + + // Reset vertex counters for next frame + lines.vCounter = 0; + lines.cCounter = 0; + triangles.vCounter = 0; + triangles.cCounter = 0; + quads.vCounter = 0; + quads.tcCounter = 0; + quads.cCounter = 0; + + // Reset depth for next draw + currentDepth = -1.0f; + + // Restore projection/modelview matrices + projection = matProjection; + modelview = matModelView; +} + +// Unload default internal buffers vertex data from CPU and GPU +static void UnloadDefaultBuffers(void) +{ + // Unbind everything + if (vaoSupported) glBindVertexArray(0); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &lines.vboId[0]); + glDeleteBuffers(1, &lines.vboId[1]); + glDeleteBuffers(1, &triangles.vboId[0]); + glDeleteBuffers(1, &triangles.vboId[1]); + glDeleteBuffers(1, &quads.vboId[0]); + glDeleteBuffers(1, &quads.vboId[1]); + glDeleteBuffers(1, &quads.vboId[2]); + glDeleteBuffers(1, &quads.vboId[3]); + + if (vaoSupported) + { + // Delete VAOs from GPU (VRAM) + glDeleteVertexArrays(1, &lines.vaoId); + glDeleteVertexArrays(1, &triangles.vaoId); + glDeleteVertexArrays(1, &quads.vaoId); + } + + // Free vertex arrays memory from CPU (RAM) + free(lines.vertices); + free(lines.colors); + + free(triangles.vertices); + free(triangles.colors); + + free(quads.vertices); + free(quads.texcoords); + free(quads.colors); + free(quads.indices); +} + +// Configure stereo rendering (including distortion shader) with HMD device parameters +static void SetStereoConfig(VrDeviceInfo hmd) +{ + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = fabsf(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.distortionK[0] + + hmd.distortionK[1]*lensRadiusSq + + hmd.distortionK[2]*lensRadiusSq*lensRadiusSq + + hmd.distortionK[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, 2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.distortionK, 4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance)*RAD2DEG + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance)*RAD2DEG; // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance)*RAD2DEG; + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // NOTE: Projection matrices must be transposed due to raymath convention + MatrixTranspose(&vrConfig.eyesProjection[0]); + MatrixTranspose(&vrConfig.eyesProjection[1]); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + //vrConfig.eyesViewport[0] = (Rectangle){ 0, 0, hmd.hResolution/2, hmd.vResolution }; + //vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; +} + +// Set internal projection and modelview matrix depending on eyes tracking data +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) +{ + if (vrEnabled) + { + Matrix eyeProjection = matProjection; + Matrix eyeModelView = matModelView; + +#if defined(RLGL_OCULUS_SUPPORT) + if (vrDeviceReady) + { + rlViewport(layer.eyeLayer.Viewport[eye].Pos.x, layer.eyeLayer.Viewport[eye].Pos.y, + layer.eyeLayer.Viewport[eye].Size.w, layer.eyeLayer.Viewport[eye].Size.h); + + Quaternion eyeRenderPose = (Quaternion){ layer.eyeLayer.RenderPose[eye].Orientation.x, + layer.eyeLayer.RenderPose[eye].Orientation.y, + layer.eyeLayer.RenderPose[eye].Orientation.z, + layer.eyeLayer.RenderPose[eye].Orientation.w }; + QuaternionInvert(&eyeRenderPose); + Matrix eyeOrientation = QuaternionToMatrix(eyeRenderPose); + Matrix eyeTranslation = MatrixTranslate(-layer.eyeLayer.RenderPose[eye].Position.x, + -layer.eyeLayer.RenderPose[eye].Position.y, + -layer.eyeLayer.RenderPose[eye].Position.z); + + Matrix eyeView = MatrixMultiply(eyeTranslation, eyeOrientation); // Matrix containing eye-head movement + eyeModelView = MatrixMultiply(matModelView, eyeView); // Combine internal camera matrix (modelview) wih eye-head movement + + eyeProjection = layer.eyeProjections[eye]; + } + else +#endif + { + // Setup viewport and projection/modelview matrices using tracking data + rlViewport(eye*screenWidth/2, 0, screenWidth/2, screenHeight); + + // Apply view offset to modelview matrix + eyeModelView = MatrixMultiply(matModelView, vrConfig.eyesViewOffset[eye]); + + eyeProjection = vrConfig.eyesProjection[eye]; + } + + SetMatrixModelview(eyeModelView); + SetMatrixProjection(eyeProjection); + } +} #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_11) @@ -3107,7 +3744,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) int mipmapCount = 1; // Required mipmap levels count (including base level) int width = baseWidth; int height = baseHeight; - int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...) + int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only // Count mipmap levels required while ((width != 1) && (height != 1)) @@ -3136,8 +3773,8 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) // Generate mipmaps // NOTE: Every mipmap data is stored after data - pixel *image = (pixel *)malloc(width*height*sizeof(pixel)); - pixel *mipmap = NULL; + Color *image = (Color *)malloc(width*height*sizeof(Color)); + Color *mipmap = NULL; int offset = 0; int j = 0; @@ -3185,15 +3822,15 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) } // Manual mipmap generation (basic scaling algorithm) -static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight) +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) { int x2, y2; - pixel prow, pcol; + Color prow, pcol; int width = srcWidth/2; int height = srcHeight/2; - pixel *mipmap = (pixel *)malloc(width*height*sizeof(pixel)); + Color *mipmap = (Color *)malloc(width*height*sizeof(Color)); // Scaling algorithm works perfectly (box-filter) for (int y = 0; y < height; y++) @@ -3227,16 +3864,313 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight) } #endif -#if defined(RLGL_STANDALONE) +#if defined(RLGL_OCULUS_SUPPORT) +// Initialize Oculus device (returns true if success) +OCULUSAPI bool InitOculusDevice(void) +{ + bool oculusReady = false; + + ovrResult result = ovr_Initialize(NULL); + + if (OVR_FAILURE(result)) TraceLog(WARNING, "OVR: Could not initialize Oculus device"); + else + { + result = ovr_Create(&session, &luid); + if (OVR_FAILURE(result)) + { + TraceLog(WARNING, "OVR: Could not create Oculus session"); + ovr_Shutdown(); + } + else + { + hmdDesc = ovr_GetHmdDesc(session); + + TraceLog(INFO, "OVR: Product Name: %s", hmdDesc.ProductName); + TraceLog(INFO, "OVR: Manufacturer: %s", hmdDesc.Manufacturer); + TraceLog(INFO, "OVR: Product ID: %i", hmdDesc.ProductId); + TraceLog(INFO, "OVR: Product Type: %i", hmdDesc.Type); + //TraceLog(INFO, "OVR: Serial Number: %s", hmdDesc.SerialNumber); + TraceLog(INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h); + + // NOTE: Oculus mirror is set to defined screenWidth and screenHeight... + // ...ideally, it should be (hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2) + // Initialize Oculus Buffers + layer = InitOculusLayer(session); + buffer = LoadOculusBuffer(session, layer.width, layer.height); + mirror = LoadOculusMirror(session, hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2); // NOTE: hardcoded... + layer.eyeLayer.ColorTexture[0] = buffer.textureChain; //SetOculusLayerTexture(eyeLayer, buffer.textureChain); + + // Recenter OVR tracking origin + ovr_RecenterTrackingOrigin(session); + + oculusReady = true; + vrEnabled = true; + } + } + + return oculusReady; +} + +// Close Oculus device (and unload buffers) +OCULUSAPI void CloseOculusDevice(void) +{ + UnloadOculusMirror(session, mirror); // Unload Oculus mirror buffer + UnloadOculusBuffer(session, buffer); // Unload Oculus texture buffers + + ovr_Destroy(session); // Free Oculus session data + ovr_Shutdown(); // Close Oculus device connection +} + +// Update Oculus head position-orientation tracking +OCULUSAPI void UpdateOculusTracking(Camera *camera) +{ + frameIndex++; + + ovrPosef eyePoses[2]; + ovr_GetEyePoses(session, frameIndex, ovrTrue, layer.viewScaleDesc.HmdToEyeOffset, eyePoses, &layer.eyeLayer.SensorSampleTime); + + layer.eyeLayer.RenderPose[0] = eyePoses[0]; + layer.eyeLayer.RenderPose[1] = eyePoses[1]; + + // TODO: Update external camera with eyePoses data (position, orientation) + // NOTE: We can simplify to simple camera if we consider IPD and HMD device configuration again later + // it will be useful for the user to draw, lets say, billboards oriented to camera + + // Get session status information + ovrSessionStatus sessionStatus; + ovr_GetSessionStatus(session, &sessionStatus); + + if (sessionStatus.ShouldQuit) TraceLog(WARNING, "OVR: Session should quit..."); + if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session); + //if (sessionStatus.HmdPresent) // HMD is present. + //if (sessionStatus.DisplayLost) // HMD was unplugged or the display driver was manually disabled or encountered a TDR. + //if (sessionStatus.HmdMounted) // HMD is on the user's head. + //if (sessionStatus.IsVisible) // the game or experience has VR focus and is visible in the HMD. +} + +// Setup Oculus buffers for drawing +OCULUSAPI void BeginOculusDrawing(void) +{ + GLuint currentTexId; + int currentIndex; + + ovr_GetTextureSwapChainCurrentIndex(session, buffer.textureChain, ¤tIndex); + ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, currentIndex, ¤tTexId); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer.fboId); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currentTexId, 0); + //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, buffer.depthId, 0); // Already binded +} + +// Finish Oculus drawing and blit framebuffer to mirror +OCULUSAPI void EndOculusDrawing(void) +{ + // Unbind current framebuffer (Oculus buffer) + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + ovr_CommitTextureSwapChain(session, buffer.textureChain); + + ovrLayerHeader *layers = &layer.eyeLayer.Header; + ovr_SubmitFrame(session, frameIndex, &layer.viewScaleDesc, &layers, 1); + + // Blit mirror texture to back buffer + BlitOculusMirror(session, mirror); +} + +// Load Oculus required buffers: texture-swap-chain, fbo, texture-depth +static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height) +{ + OculusBuffer buffer; + buffer.width = width; + buffer.height = height; + + // Create OVR texture chain + ovrTextureSwapChainDesc desc = {}; + desc.Type = ovrTexture_2D; + desc.ArraySize = 1; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; // Requires glEnable(GL_FRAMEBUFFER_SRGB); + desc.SampleCount = 1; + desc.StaticImage = ovrFalse; + + ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &buffer.textureChain); + + if (!OVR_SUCCESS(result)) TraceLog(WARNING, "OVR: Failed to create swap textures buffer"); + + int textureCount = 0; + ovr_GetTextureSwapChainLength(session, buffer.textureChain, &textureCount); + + if (!OVR_SUCCESS(result) || !textureCount) TraceLog(WARNING, "OVR: Unable to count swap chain textures"); + + for (int i = 0; i < textureCount; ++i) + { + GLuint chainTexId; + ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, i, &chainTexId); + glBindTexture(GL_TEXTURE_2D, chainTexId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glBindTexture(GL_TEXTURE_2D, 0); + + /* + // Setup framebuffer object (using depth texture) + glGenFramebuffers(1, &buffer.fboId); + glGenTextures(1, &buffer.depthId); + glBindTexture(GL_TEXTURE_2D, buffer.depthId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, buffer.width, buffer.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + */ + + // Setup framebuffer object (using depth renderbuffer) + glGenFramebuffers(1, &buffer.fboId); + glGenRenderbuffers(1, &buffer.depthId); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer.fboId); + glBindRenderbuffer(GL_RENDERBUFFER, buffer.depthId); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, buffer.width, buffer.height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer.depthId); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + return buffer; +} + +// Unload texture required buffers +static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer) +{ + if (buffer.textureChain) + { + ovr_DestroyTextureSwapChain(session, buffer.textureChain); + buffer.textureChain = NULL; + } + + if (buffer.depthId != 0) glDeleteTextures(1, &buffer.depthId); + if (buffer.fboId != 0) glDeleteFramebuffers(1, &buffer.fboId); +} + +// Load Oculus mirror buffers +static OculusMirror LoadOculusMirror(ovrSession session, int width, int height) +{ + OculusMirror mirror; + mirror.width = width; + mirror.height = height; + + ovrMirrorTextureDesc mirrorDesc; + memset(&mirrorDesc, 0, sizeof(mirrorDesc)); + mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; + mirrorDesc.Width = mirror.width; + mirrorDesc.Height = mirror.height; + + if (!OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirror.texture))) TraceLog(WARNING, "Could not create mirror texture"); + + glGenFramebuffers(1, &mirror.fboId); + + return mirror; +} + +// Unload Oculus mirror buffers +static void UnloadOculusMirror(ovrSession session, OculusMirror mirror) +{ + if (mirror.fboId != 0) glDeleteFramebuffers(1, &mirror.fboId); + if (mirror.texture) ovr_DestroyMirrorTexture(session, mirror.texture); +} + +// Copy Oculus screen buffer to mirror texture +static void BlitOculusMirror(ovrSession session, OculusMirror mirror) +{ + GLuint mirrorTextureId; + + ovr_GetMirrorTextureBufferGL(session, mirror.texture, &mirrorTextureId); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, mirror.fboId); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTextureId, 0); +#if defined(GRAPHICS_API_OPENGL_33) + // NOTE: glBlitFramebuffer() requires extension: GL_EXT_framebuffer_blit (not available in OpenGL ES 2.0) + glBlitFramebuffer(0, 0, mirror.width, mirror.height, 0, mirror.height, mirror.width, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); +#endif + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +} + +// Init Oculus layer (similar to photoshop) +static OculusLayer InitOculusLayer(ovrSession session) +{ + OculusLayer layer = { 0 }; + + layer.viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + + memset(&layer.eyeLayer, 0, sizeof(ovrLayerEyeFov)); + layer.eyeLayer.Header.Type = ovrLayerType_EyeFov; + layer.eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; + + ovrEyeRenderDesc eyeRenderDescs[2]; + + for (int eye = 0; eye < 2; eye++) + { + eyeRenderDescs[eye] = ovr_GetRenderDesc(session, eye, hmdDesc.DefaultEyeFov[eye]); + ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(eyeRenderDescs[eye].Fov, 0.01f, 10000.0f, ovrProjection_None); //ovrProjection_ClipRangeOpenGL); + layer.eyeProjections[eye] = FromOvrMatrix(ovrPerspectiveProjection); // NOTE: struct ovrMatrix4f { float M[4][4] } --> struct Matrix + + layer.viewScaleDesc.HmdToEyeOffset[eye] = eyeRenderDescs[eye].HmdToEyeOffset; + layer.eyeLayer.Fov[eye] = eyeRenderDescs[eye].Fov; + + ovrSizei eyeSize = ovr_GetFovTextureSize(session, eye, layer.eyeLayer.Fov[eye], 1.0f); + layer.eyeLayer.Viewport[eye].Size = eyeSize; + layer.eyeLayer.Viewport[eye].Pos.x = layer.width; + layer.eyeLayer.Viewport[eye].Pos.y = 0; + + layer.height = eyeSize.h; //std::max(renderTargetSize.y, (uint32_t)eyeSize.h); + layer.width += eyeSize.w; + } + + return layer; +} + +// Convert from Oculus ovrMatrix4f struct to raymath Matrix struct +static Matrix FromOvrMatrix(ovrMatrix4f ovrmat) +{ + Matrix rmat; + + rmat.m0 = ovrmat.M[0][0]; + rmat.m1 = ovrmat.M[1][0]; + rmat.m2 = ovrmat.M[2][0]; + rmat.m3 = ovrmat.M[3][0]; + rmat.m4 = ovrmat.M[0][1]; + rmat.m5 = ovrmat.M[1][1]; + rmat.m6 = ovrmat.M[2][1]; + rmat.m7 = ovrmat.M[3][1]; + rmat.m8 = ovrmat.M[0][2]; + rmat.m9 = ovrmat.M[1][2]; + rmat.m10 = ovrmat.M[2][2]; + rmat.m11 = ovrmat.M[3][2]; + rmat.m12 = ovrmat.M[0][3]; + rmat.m13 = ovrmat.M[1][3]; + rmat.m14 = ovrmat.M[2][3]; + rmat.m15 = ovrmat.M[3][3]; + + MatrixTranspose(&rmat); + + return rmat; +} +#endif + +#if defined(RLGL_STANDALONE) // Output a trace log message // NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning -static void TraceLog(int msgType, const char *text, ...) +void TraceLog(int msgType, const char *text, ...) { va_list args; va_start(args, text); - switch(msgType) + switch (msgType) { case INFO: fprintf(stdout, "INFO: "); break; case ERROR: fprintf(stdout, "ERROR: "); break; @@ -3252,4 +4186,32 @@ static void TraceLog(int msgType, const char *text, ...) if (msgType == ERROR) exit(1); } + +// Converts Matrix to float array +// NOTE: Returned vector is a transposed version of the Matrix struct, +// it should be this way because, despite raymath use OpenGL column-major convention, +// Matrix struct memory alignment and variables naming are not coherent +float *MatrixToFloat(Matrix mat) +{ + static float buffer[16]; + + buffer[0] = mat.m0; + buffer[1] = mat.m4; + buffer[2] = mat.m8; + buffer[3] = mat.m12; + buffer[4] = mat.m1; + buffer[5] = mat.m5; + buffer[6] = mat.m9; + buffer[7] = mat.m13; + buffer[8] = mat.m2; + buffer[9] = mat.m6; + buffer[10] = mat.m10; + buffer[11] = mat.m14; + buffer[12] = mat.m3; + buffer[13] = mat.m7; + buffer[14] = mat.m11; + buffer[15] = mat.m15; + + return buffer; +} #endif |
