diff --git a/examples/oculus_glfw_sample/oculus_glfw_sample.c b/examples/oculus_glfw_sample/oculus_glfw_sample.c index 19de0188c..b1fabbe97 100644 --- a/examples/oculus_glfw_sample/oculus_glfw_sample.c +++ b/examples/oculus_glfw_sample/oculus_glfw_sample.c @@ -17,57 +17,52 @@ * ********************************************************************************************/ -#if defined(_WIN32) - #define GLFW_EXPOSE_NATIVE_WIN32 - #define GLFW_EXPOSE_NATIVE_WGL - #define OVR_OS_WIN32 -#elif defined(__APPLE__) - #define GLFW_EXPOSE_NATIVE_COCOA - #define GLFW_EXPOSE_NATIVE_NSGL - #define OVR_OS_MAC -#elif defined(__linux__) - #define GLFW_EXPOSE_NATIVE_X11 - #define GLFW_EXPOSE_NATIVE_GLX - #define OVR_OS_LINUX -#endif +#include +#include +#include +#include -#include "glad.h" // Extensions loading library - -#include -#include +#include "glad.h" // Extensions loading library +#include // Windows/Context and inputs management #include "OculusSDK/LibOVR/Include/OVR_CAPI_GL.h" // Oculus SDK for OpenGL -//#include "GL/CAPI_GLE.h" // stripped-down GLEW/GLAD library to manage extensions (really required?) -//#include "Extras/OVR_Math.h" // math utilities C++ (really required?) - #define RLGL_STANDALONE #include "rlgl.h" -#include -#include +// OVR device variables +ovrSession session; +ovrHmdDesc hmdDesc; +ovrGraphicsLuid luid; + +// OVR OpenGL required variables +GLuint fbo = 0; +GLuint depthBuffer = 0; +ovrTextureSwapChain eyeTexture; + +GLuint mirrorFbo = 0; +ovrMirrorTexture mirrorTexture; +ovrEyeRenderDesc eyeRenderDescs[2]; +Matrix eyeProjections[2]; + +ovrLayerEyeFov eyeLayer; +ovrViewScaleDesc viewScaleDesc; + +Vector2 renderTargetSize = { 0, 0 }; +Vector2 mirrorSize; +unsigned int frame = 0; + +// GLFW variables +GLFWwindow *window = NULL; //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef struct OculusBuffer { - ovrTextureSwapChain textureChain; - GLuint depthId; - GLuint fboId; - int width; - int height; -} OculusBuffer; - typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height); -static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer); -static void SetOculusBuffer(ovrSession session, OculusBuffer buffer); -static void UnsetOculusBuffer(OculusBuffer buffer); - static void ErrorCallback(int error, const char* description) { fputs(description, stderr); @@ -83,18 +78,15 @@ static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, i static void DrawRectangleV(Vector2 position, Vector2 size, Color color); static void TraceLog(int msgType, const char *text, ...); +static Matrix FromOvrMatrix(ovrMatrix4f ovrM); +void DrawGrid(int slices, float spacing); +void DrawCube(Vector3 position, float width, float height, float length, Color color); //---------------------------------------------------------------------------------- // Main Entry point //---------------------------------------------------------------------------------- -int main() +int main() { - // Initialization - //-------------------------------------------------------------------------------------- - ovrSession session; - ovrGraphicsLuid luid; // Useless for OpenGL since SDK 0.7 - ovrHmdDesc hmdDesc; - ovrResult result = ovr_Initialize(NULL); if (OVR_FAILURE(result)) TraceLog(LOG_ERROR, "OVR: Could not initialize Oculus device"); @@ -114,15 +106,37 @@ int main() TraceLog(LOG_INFO, "OVR: Serian Number: %s", hmdDesc.SerialNumber); TraceLog(LOG_INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h); - int screenWidth = hmdDesc.Resolution.w/2 + 100; // Added 100 pixels for testing - int screenHeight = hmdDesc.Resolution.h/2 + 100; // Added 100 pixels for testing + + viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + memset(&eyeLayer, 0, sizeof(ovrLayerEyeFov)); + eyeLayer.Header.Type = ovrLayerType_EyeFov; + eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; + + 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, 1000.0f, ovrProjection_ClipRangeOpenGL); + // NOTE struct ovrMatrix4f { float M[4][4] } + eyeProjections[eye] = FromOvrMatrix(ovrPerspectiveProjection); + viewScaleDesc.HmdToEyeOffset[eye] = eyeRenderDescs[eye].HmdToEyeOffset; + eyeLayer.Fov[eye] = eyeRenderDescs[eye].Fov; + ovrSizei eyeSize = ovr_GetFovTextureSize(session, eye, eyeLayer.Fov[eye], 1.0f); + eyeLayer.Viewport[eye].Size = eyeSize; + eyeLayer.Viewport[eye].Pos.x = renderTargetSize.x; + eyeLayer.Viewport[eye].Pos.y = 0; + + renderTargetSize.y = eyeSize.h; //std::max(renderTargetSize.y, (uint32_t)eyeSize.h); + renderTargetSize.x += eyeSize.w; + } + + // Make the on screen window 1/2 the resolution of the device + mirrorSize.x = hmdDesc.Resolution.w/2; + mirrorSize.y = hmdDesc.Resolution.h/2; + + // GLFW3 Initialization + OpenGL 3.3 Context + Extensions //-------------------------------------------------------- - GLFWwindow *window; - - glfwSetErrorCallback(ErrorCallback); - if (!glfwInit()) { TraceLog(LOG_WARNING, "GLFW3: Can not initialize GLFW"); @@ -133,12 +147,11 @@ int main() glfwWindowHint(GLFW_DEPTH_BITS, 16); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); - glfwWindowHint(GLFW_DECORATED, GL_FALSE); // Mandatory on Oculus Rift to avoid program crash! + //glfwWindowHint(GLFW_DECORATED, GL_FALSE); // Mandatory on Oculus Rift to avoid program crash? --> NO - window = glfwCreateWindow(screenWidth, screenHeight, "rlgl standalone", NULL, NULL); + window = glfwCreateWindow(mirrorSize.x, mirrorSize.y, "raylib oculus sample", NULL, NULL); if (!window) { @@ -147,6 +160,7 @@ int main() } else TraceLog(LOG_INFO, "GLFW3: Window created successfully"); + glfwSetErrorCallback(ErrorCallback); glfwSetKeyCallback(window, KeyCallback); glfwMakeContextCurrent(window); @@ -159,174 +173,132 @@ int main() } else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); - rlglInit(); - rlglInitGraphics(0, 0, screenWidth, screenHeight); - rlClearColor(245, 245, 245, 255); // Define clear color + // Initialize OVR OpenGL swap chain textures + ovrTextureSwapChainDesc desc = {}; + desc.Type = ovrTexture_2D; + desc.ArraySize = 1; + desc.Width = renderTargetSize.x; + desc.Height = renderTargetSize.y; + desc.MipLevels = 1; + desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; + desc.SampleCount = 1; + desc.StaticImage = ovrFalse; - Vector2 position = { screenWidth/2 - 100, screenHeight/2 - 100 }; - Vector2 size = { 200, 200 }; - Color color = { 180, 20, 20, 255 }; - //--------------------------------------------------------------------------- + result = ovr_CreateTextureSwapChainGL(session, &desc, &eyeTexture); + eyeLayer.ColorTexture[0] = eyeTexture; - OculusBuffer eyeRenderBuffer[2]; - - GLuint mirrorFBO = 0; - ovrMirrorTexture mirrorTexture = NULL; - - bool isVisible = true; - long long frameIndex = 0; - - // Make eyes render buffers - ovrSizei recommendedTexSizeLeft = ovr_GetFovTextureSize(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0], 1.0f); - eyeRenderBuffer[0] = LoadOculusBuffer(session, recommendedTexSizeLeft.w, recommendedTexSizeLeft.h); - ovrSizei recommendedTexSizeRight = ovr_GetFovTextureSize(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1], 1.0f); - eyeRenderBuffer[1] = LoadOculusBuffer(session, recommendedTexSizeRight.w, recommendedTexSizeRight.h); + if (!OVR_SUCCESS(result)) TraceLog(LOG_WARNING, "Failed to create swap textures"); - // Note: the mirror window can be any size, for this sample we use 1/2 the HMD resolution - ovrSizei windowSize = { hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2 }; - - // Define mirror texture descriptor - ovrMirrorTextureDesc mirrorDesc; - memset(&mirrorDesc, 0, sizeof(mirrorDesc)); - mirrorDesc.Width = windowSize.w; - mirrorDesc.Height = windowSize.h; - mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; - - // Create mirror texture and an FBO used to copy mirror texture to back buffer - result = ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirrorTexture); - if (!OVR_SUCCESS(result)) TraceLog(LOG_WARNING, "OVR: Failed to create mirror texture"); - - // Configure the mirror read buffer - GLuint texId; - ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &texId); - - glGenFramebuffers(1, &mirrorFBO); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0); - glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + int length = 0; + result = ovr_GetTextureSwapChainLength(session, eyeTexture, &length); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + if (!OVR_SUCCESS(result) || !length) TraceLog(LOG_WARNING, "Unable to count swap chain textures"); + + for (int i = 0; i < length; ++i) { - glDeleteFramebuffers(1, &mirrorFBO); - TraceLog(LOG_WARNING, "OVR: Could not initialize mirror framebuffers"); + GLuint chainTexId; + ovr_GetTextureSwapChainBufferGL(session, eyeTexture, 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); } - glClearColor(1.0f, 0.1f, 0.1f, 0.0f); - glEnable(GL_DEPTH_TEST); + glBindTexture(GL_TEXTURE_2D, 0); + + // Setup framebuffer object + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &depthBuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, renderTargetSize.x, renderTargetSize.y); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + // Setup mirror texture + ovrMirrorTextureDesc mirrorDesc; + memset(&mirrorDesc, 0, sizeof(mirrorDesc)); + mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; + mirrorDesc.Width = mirrorSize.x; + mirrorDesc.Height = mirrorSize.y; + + if (!OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirrorTexture))) TraceLog(LOG_WARNING, "Could not create mirror texture"); + + glGenFramebuffers(1, &mirrorFbo); + + // Recenter OVR tracking origin ovr_RecenterTrackingOrigin(session); - // FloorLevel will give tracking poses where the floor height is 0 - ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel); - //-------------------------------------------------------------------------------------- + // Initialize rlgl internal buffers and OpenGL state + rlglInit(); + rlglInitGraphics(0, 0, mirrorSize.x, mirrorSize.y); + rlClearColor(245, 245, 245, 255); // Define clear color + glEnable(GL_DEPTH_TEST); + + Vector2 position = { mirrorSize.x/2 - 100, mirrorSize.y/2 - 100 }; + Vector2 size = { 200, 200 }; + Color color = { 180, 20, 20, 255 }; + Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - // Main loop - while (!glfwWindowShouldClose(window)) + while (!glfwWindowShouldClose(window)) { // Update //---------------------------------------------------------------------------------- - frameIndex++; + frame++; - // TODO: Update game here! - - // Call ovr_GetRenderDesc each frame to get the ovrEyeRenderDesc, as the returned values (e.g. HmdToEyeOffset) may change at runtime. - ovrEyeRenderDesc eyeRenderDesc[2]; - eyeRenderDesc[0] = ovr_GetRenderDesc(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0]); - eyeRenderDesc[1] = ovr_GetRenderDesc(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1]); - - // Get eye poses, feeding in correct IPD offset - ovrPosef eyeRenderPose[2]; - ovrVector3f hmdToEyeOffset[2] = { eyeRenderDesc[0].HmdToEyeOffset, eyeRenderDesc[1].HmdToEyeOffset }; - - double sensorSampleTime; // sensorSampleTime is fed into the layer later - ovr_GetEyePoses(session, frameIndex, ovrTrue, hmdToEyeOffset, eyeRenderPose, &sensorSampleTime); + ovrPosef eyePoses[2]; + ovr_GetEyePoses(session, frame, ovrTrue, viewScaleDesc.HmdToEyeOffset, eyePoses, &eyeLayer.SensorSampleTime); //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- + int curIndex; + ovr_GetTextureSwapChainCurrentIndex(session, eyeTexture, &curIndex); + GLuint curTexId; + ovr_GetTextureSwapChainBufferGL(session, eyeTexture, curIndex, &curTexId); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, curTexId, 0); - // Clear screen to red color - //glClearColor(1.0f, 0.1f, 0.1f, 0.0f); - //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - if (isVisible) - { - for (int eye = 0; eye < 2; ++eye) - { - SetOculusBuffer(session, eyeRenderBuffer[eye]); - - // TODO: Get view and projection matrices for the eye - // Sample using Oculus OVR_Math.h (C++) - /* - Matrix4f projection[eye] = Matrix4f(ovrMatrix4f_Projection(eyeRenderDesc[eye].Fov, 0.01f, 10000.0f, ovrProjection_None)); - Matrix4f eyeOrientation[eye] = Matrix4f(Quatf(eyeRenderPose[eye].Orientation).Inverted()); - Matrix4f eyePose[eye] = Matrix4f::Translation(-Vector3f(eyeRenderPose[eye].Position)); - Matrix4f mvp = projection[eye]*eyeOrientation[eye]*eyePose[eye]; - */ - - // Sample using custom raymath.h (C) -INCOMPLETE- - /* - Matrix projection = MatrixPerspective(eyeRenderDesc[eye].Fov, ((double)screenWidth/(double)screenHeight), 0.01, 1000.0); - Matrix eyeOrientation = QuaternionToMatrix((Quaternion){ -eyeRenderPose[eye].Orientation.x, -eyeRenderPose[eye].Orientation.y, - -eyeRenderPose[eye].Orientation.z, -eyeRenderPose[eye].Orientation.w }); - Matrix eyePose = MatrixTranslate(-eyeRenderPose[eye].Position.x, -eyeRenderPose[eye].Position.y, -eyeRenderPose[eye].Position.z); - Matrix mvp = MatrixMultiply(projection, MatrixMultiply(eyeOrientation, eyePose)); - */ - - // Render everything - // TODO: Pass calculated mvp matrix to default shader to consider projection and orientation! - //DrawRectangleV(position, size, color); - //rlglDraw(); - - UnsetOculusBuffer(eyeRenderBuffer[eye]); - - // Commit changes to the textures so they get picked up frame - ovr_CommitTextureSwapChain(session, eyeRenderBuffer[eye].textureChain); - } - } - - // Set up positional data - ovrViewScaleDesc viewScaleDesc; - viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; - viewScaleDesc.HmdToEyeOffset[0] = hmdToEyeOffset[0]; - viewScaleDesc.HmdToEyeOffset[1] = hmdToEyeOffset[1]; - - // Create the main eye layer - ovrLayerEyeFov eyeLayer; - eyeLayer.Header.Type = ovrLayerType_EyeFov; - eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; // Because OpenGL - for (int eye = 0; eye < 2; eye++) { - eyeLayer.ColorTexture[eye] = eyeRenderBuffer[eye].textureChain; - eyeLayer.Viewport[eye] = (ovrRecti){ eyeRenderBuffer[eye].width, eyeRenderBuffer[eye].height }; - eyeLayer.Fov[eye] = hmdDesc.DefaultEyeFov[eye]; - eyeLayer.RenderPose[eye] = eyeRenderPose[eye]; - eyeLayer.SensorSampleTime = sensorSampleTime; + glViewport(eyeLayer.Viewport[eye].Pos.x, eyeLayer.Viewport[eye].Pos.y, + eyeLayer.Viewport[eye].Size.w, eyeLayer.Viewport[eye].Size.h); + eyeLayer.RenderPose[eye] = eyePoses[eye]; + + // Convert struct ovrPosef { ovrQuatf Orientation; ovrVector3f Position; } to Matrix + // TODO: Review maths! + Matrix eyeOrientation = QuaternionToMatrix((Quaternion){ -eyePoses[eye].Orientation.x, -eyePoses[eye].Orientation.y, -eyePoses[eye].Orientation.z, -eyePoses[eye].Orientation.w }); + Matrix eyePosition = MatrixTranslate(-eyePoses[eye].Position.x, -eyePoses[eye].Position.y, -eyePoses[eye].Position.z); + Matrix mvp = MatrixMultiply(eyeProjections[eye], MatrixMultiply(eyeOrientation, eyePosition)); + + // NOTE: Nothing is drawn until rlglDraw() + DrawRectangleV(position, size, color); + //DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, color); + //DrawGrid(10, 1.0f); + + // NOTE: rlglDraw() must be modified to support an external modelview-projection matrix + // TODO: Still working on it (now uses internal mvp) + rlglDraw(mvp); } - - // Append all the layers to global list - ovrLayerHeader *layerList = &eyeLayer.Header; - ovrResult result = ovr_SubmitFrame(session, frameIndex, NULL, &layerList, 1); - // exit the rendering loop if submit returns an error, will retry on ovrError_DisplayLost - if (!OVR_SUCCESS(result)) return 1; - - isVisible = (result == ovrSuccess); - - // Get session status information - ovrSessionStatus sessionStatus; - ovr_GetSessionStatus(session, &sessionStatus); - if (sessionStatus.ShouldQuit) TraceLog(LOG_WARNING, "OVR: Session should quit."); - if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session); - - // Blit mirror texture to back buffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - GLint w = mirrorDesc.Width; - GLint h = mirrorDesc.Height; - glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + ovr_CommitTextureSwapChain(session, eyeTexture); + ovrLayerHeader *headerList = &eyeLayer.Header; + ovr_SubmitFrame(session, frame, &viewScaleDesc, &headerList, 1); + + // Blit mirror texture to back buffer + GLuint mirrorTextureId; + ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &mirrorTextureId); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTextureId, 0); + glBlitFramebuffer(0, 0, mirrorSize.x, mirrorSize.y, 0, mirrorSize.y, mirrorSize.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glfwSwapBuffers(window); glfwPollEvents(); @@ -335,10 +307,13 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - if (mirrorFBO) glDeleteFramebuffers(1, &mirrorFBO); + if (mirrorFbo) glDeleteFramebuffers(1, &mirrorFbo); if (mirrorTexture) ovr_DestroyMirrorTexture(session, mirrorTexture); - for (int eye = 0; eye < 2; eye++) UnloadOculusBuffer(session, eyeRenderBuffer[eye]); - + + if (fbo) glDeleteFramebuffers(1, &fbo); + if (depthBuffer) glDeleteTextures(1, &depthBuffer); + if (eyeTexture) ovr_DestroyTextureSwapChain(session, eyeTexture); + rlglClose(); glfwDestroyWindow(window); @@ -355,108 +330,6 @@ int main() // Module specific Functions Definitions //---------------------------------------------------------------------------------- -// 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; - desc.SampleCount = 1; - desc.StaticImage = ovrFalse; - - ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &buffer.textureChain); - - int textureCount = 0; - ovr_GetTextureSwapChainLength(session, buffer.textureChain, &textureCount); - - if (OVR_SUCCESS(result)) - { - 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); - } - } - - // Generate framebuffer - glGenFramebuffers(1, &buffer.fboId); - - // Create Depth texture - 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); - - 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) - { - glDeleteTextures(1, &buffer.depthId); - buffer.depthId = 0; - } - - if (buffer.fboId) - { - glDeleteFramebuffers(1, &buffer.fboId); - buffer.fboId = 0; - } -} - -// Set current Oculus buffer -static void SetOculusBuffer(ovrSession session, OculusBuffer buffer) -{ - GLuint currentTexId; - int currentIndex; - - ovr_GetTextureSwapChainCurrentIndex(session, buffer.textureChain, ¤tIndex); - ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, currentIndex, ¤tTexId); - - glBindFramebuffer(GL_FRAMEBUFFER, buffer.fboId); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currentTexId, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, buffer.depthId, 0); - - glViewport(0, 0, buffer.width, buffer.height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_FRAMEBUFFER_SRGB); -} - -// Unset Oculus buffer -static void UnsetOculusBuffer(OculusBuffer buffer) -{ - glBindFramebuffer(GL_FRAMEBUFFER, buffer.fboId); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); -} - // Draw rectangle using rlgl OpenGL 1.1 style coding (translated to OpenGL 3.3 internally) static void DrawRectangleV(Vector2 position, Vector2 size, Color color) { @@ -495,4 +368,137 @@ static void TraceLog(int msgType, const char *text, ...) va_end(args); //if (msgType == LOG_ERROR) exit(1); -} \ No newline at end of file +} + +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; +} + +// Draw cube +// NOTE: Cube position is the center position +void DrawCube(Vector3 position, float width, float height, float length, Color color) +{ + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + + rlPushMatrix(); + + // NOTE: Be careful! Function order matters (rotate -> scale -> translate) + rlTranslatef(position.x, position.y, position.z); + //rlScalef(2.0f, 2.0f, 2.0f); + //rlRotatef(45, 0, 1, 0); + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // Front Face ----------------------------------------------------- + rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left + + rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right + + // Back Face ------------------------------------------------------ + rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left + rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right + + rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right + rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left + + // Top Face ------------------------------------------------------- + rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left + rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left + rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right + + rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right + rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left + rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right + + // Bottom Face ---------------------------------------------------- + rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left + rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right + rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left + + rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Right + rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right + rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left + + // Right face ----------------------------------------------------- + rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right + rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left + + rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left + rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right + rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left + + // Left Face ------------------------------------------------------ + rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right + rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left + rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right + + rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left + rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left + rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right + rlEnd(); + rlPopMatrix(); +} + +// Draw a grid centered at (0, 0, 0) +void DrawGrid(int slices, float spacing) +{ + int halfSlices = slices / 2; + + rlBegin(RL_LINES); + for(int i = -halfSlices; i <= halfSlices; i++) + { + if (i == 0) + { + rlColor3f(0.5f, 0.5f, 0.5f); + rlColor3f(0.5f, 0.5f, 0.5f); + rlColor3f(0.5f, 0.5f, 0.5f); + rlColor3f(0.5f, 0.5f, 0.5f); + } + else + { + rlColor3f(0.75f, 0.75f, 0.75f); + rlColor3f(0.75f, 0.75f, 0.75f); + rlColor3f(0.75f, 0.75f, 0.75f); + rlColor3f(0.75f, 0.75f, 0.75f); + } + + rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing); + rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing); + + rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing); + rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing); + } + rlEnd(); +} diff --git a/examples/oculus_glfw_sample/oculus_glfw_sample.old.c b/examples/oculus_glfw_sample/oculus_glfw_sample.old.c new file mode 100644 index 000000000..c4997eae5 --- /dev/null +++ b/examples/oculus_glfw_sample/oculus_glfw_sample.old.c @@ -0,0 +1,498 @@ +/******************************************************************************************* +* +* raylib Oculus minimum sample (OpenGL 3.3 Core) +* +* NOTE: This example requires raylib module [rlgl] +* +* Compile rlgl using: +* gcc -c rlgl.c -Wall -std=c99 -DRLGL_STANDALONE -DRAYMATH_IMPLEMENTATION -DGRAPHICS_API_OPENGL_33 +* +* Compile example using: +* gcc -o oculus_glfw_sample.exe oculus_glfw_sample.c rlgl.o glad.o -L. -lLibOVRRT32_1 -lglfw3 -lopengl32 -lgdi32 -std=c99 +* +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#if defined(_WIN32) + #define GLFW_EXPOSE_NATIVE_WIN32 + #define GLFW_EXPOSE_NATIVE_WGL + #define OVR_OS_WIN32 +#elif defined(__APPLE__) + #define GLFW_EXPOSE_NATIVE_COCOA + #define GLFW_EXPOSE_NATIVE_NSGL + #define OVR_OS_MAC +#elif defined(__linux__) + #define GLFW_EXPOSE_NATIVE_X11 + #define GLFW_EXPOSE_NATIVE_GLX + #define OVR_OS_LINUX +#endif + +#include "glad.h" // Extensions loading library + +#include +#include + +#include "OculusSDK/LibOVR/Include/OVR_CAPI_GL.h" // Oculus SDK for OpenGL + +//#include "GL/CAPI_GLE.h" // stripped-down GLEW/GLAD library to manage extensions (really required?) +//#include "Extras/OVR_Math.h" // math utilities C++ (really required?) + +#define RLGL_STANDALONE +#include "rlgl.h" + +#include +#include + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct OculusBuffer { + ovrTextureSwapChain textureChain; + GLuint depthId; + GLuint fboId; + int width; + int height; +} OculusBuffer; + +typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height); +static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer); +static void SetOculusBuffer(ovrSession session, OculusBuffer buffer); +static void UnsetOculusBuffer(OculusBuffer buffer); + +static void ErrorCallback(int error, const char* description) +{ + fputs(description, stderr); +} + +static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + { + glfwSetWindowShouldClose(window, GL_TRUE); + } +} + +static void DrawRectangleV(Vector2 position, Vector2 size, Color color); +static void TraceLog(int msgType, const char *text, ...); + +//---------------------------------------------------------------------------------- +// Main Entry point +//---------------------------------------------------------------------------------- +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + ovrSession session; + ovrGraphicsLuid luid; // Useless for OpenGL since SDK 0.7 + ovrHmdDesc hmdDesc; + + ovrResult result = ovr_Initialize(NULL); + if (OVR_FAILURE(result)) TraceLog(LOG_ERROR, "OVR: Could not initialize Oculus device"); + + result = ovr_Create(&session, &luid); + if (OVR_FAILURE(result)) + { + TraceLog(LOG_WARNING, "OVR: Could not create Oculus session"); + ovr_Shutdown(); + } + + hmdDesc = ovr_GetHmdDesc(session); + + TraceLog(LOG_INFO, "OVR: Product Name: %s", hmdDesc.ProductName); + TraceLog(LOG_INFO, "OVR: Manufacturer: %s", hmdDesc.Manufacturer); + TraceLog(LOG_INFO, "OVR: Product ID: %i", hmdDesc.ProductId); + TraceLog(LOG_INFO, "OVR: Product Type: %i", hmdDesc.Type); + TraceLog(LOG_INFO, "OVR: Serian Number: %s", hmdDesc.SerialNumber); + TraceLog(LOG_INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h); + + int screenWidth = hmdDesc.Resolution.w/2 + 100; // Added 100 pixels for testing + int screenHeight = hmdDesc.Resolution.h/2 + 100; // Added 100 pixels for testing + + // GLFW3 Initialization + OpenGL 3.3 Context + Extensions + //-------------------------------------------------------- + GLFWwindow *window; + + glfwSetErrorCallback(ErrorCallback); + + if (!glfwInit()) + { + TraceLog(LOG_WARNING, "GLFW3: Can not initialize GLFW"); + exit(EXIT_FAILURE); + } + else TraceLog(LOG_INFO, "GLFW3: GLFW initialized successfully"); + + glfwWindowHint(GLFW_DEPTH_BITS, 16); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + glfwWindowHint(GLFW_DECORATED, GL_FALSE); // Mandatory on Oculus Rift to avoid program crash! + + window = glfwCreateWindow(screenWidth, screenHeight, "rlgl standalone", NULL, NULL); + + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + else TraceLog(LOG_INFO, "GLFW3: Window created successfully"); + + glfwSetKeyCallback(window, KeyCallback); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + TraceLog(LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + exit(1); + } + else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); + + rlglInit(); + rlglInitGraphics(0, 0, screenWidth, screenHeight); + rlClearColor(245, 245, 245, 255); // Define clear color + + Vector2 position = { screenWidth/2 - 100, screenHeight/2 - 100 }; + Vector2 size = { 200, 200 }; + Color color = { 180, 20, 20, 255 }; + //--------------------------------------------------------------------------- + + OculusBuffer eyeRenderBuffer[2]; + + GLuint mirrorFBO = 0; + ovrMirrorTexture mirrorTexture = NULL; + + bool isVisible = true; + long long frameIndex = 0; + + // Make eyes render buffers + ovrSizei recommendedTexSizeLeft = ovr_GetFovTextureSize(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0], 1.0f); + eyeRenderBuffer[0] = LoadOculusBuffer(session, recommendedTexSizeLeft.w, recommendedTexSizeLeft.h); + ovrSizei recommendedTexSizeRight = ovr_GetFovTextureSize(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1], 1.0f); + eyeRenderBuffer[1] = LoadOculusBuffer(session, recommendedTexSizeRight.w, recommendedTexSizeRight.h); + + // Note: the mirror window can be any size, for this sample we use 1/2 the HMD resolution + ovrSizei windowSize = { hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2 }; + + // Define mirror texture descriptor + ovrMirrorTextureDesc mirrorDesc; + memset(&mirrorDesc, 0, sizeof(mirrorDesc)); + mirrorDesc.Width = windowSize.w; + mirrorDesc.Height = windowSize.h; + mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; + + // Create mirror texture and an FBO used to copy mirror texture to back buffer + result = ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirrorTexture); + if (!OVR_SUCCESS(result)) TraceLog(LOG_WARNING, "OVR: Failed to create mirror texture"); + + // Configure the mirror read buffer + GLuint texId; + ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &texId); + + glGenFramebuffers(1, &mirrorFBO); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0); + glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteFramebuffers(1, &mirrorFBO); + TraceLog(LOG_WARNING, "OVR: Could not initialize mirror framebuffers"); + } + + glClearColor(1.0f, 0.1f, 0.1f, 0.0f); + glEnable(GL_DEPTH_TEST); + ovr_RecenterTrackingOrigin(session); + + // FloorLevel will give tracking poses where the floor height is 0 + ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel); + //-------------------------------------------------------------------------------------- + + // Main loop + while (!glfwWindowShouldClose(window)) + { + // Update + //---------------------------------------------------------------------------------- + frameIndex++; + + // TODO: Update game here! + + // Call ovr_GetRenderDesc each frame to get the ovrEyeRenderDesc, as the returned values (e.g. HmdToEyeOffset) may change at runtime. + ovrEyeRenderDesc eyeRenderDesc[2]; + eyeRenderDesc[0] = ovr_GetRenderDesc(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0]); + eyeRenderDesc[1] = ovr_GetRenderDesc(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1]); + + // Get eye poses, feeding in correct IPD offset + ovrPosef eyeRenderPose[2]; + ovrVector3f hmdToEyeOffset[2] = { eyeRenderDesc[0].HmdToEyeOffset, eyeRenderDesc[1].HmdToEyeOffset }; + + double sensorSampleTime; // sensorSampleTime is fed into the layer later + ovr_GetEyePoses(session, frameIndex, ovrTrue, hmdToEyeOffset, eyeRenderPose, &sensorSampleTime); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + + // Clear screen to red color + glClearColor(1.0f, 0.1f, 0.1f, 0.0f); + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (isVisible) + { + for (int eye = 0; eye < 2; ++eye) + { + SetOculusBuffer(session, eyeRenderBuffer[eye]); + + // TODO: Get view and projection matrices for the eye + // Sample using Oculus OVR_Math.h (C++) + /* + Matrix4f projection[eye] = Matrix4f(ovrMatrix4f_Projection(eyeRenderDesc[eye].Fov, 0.01f, 10000.0f, ovrProjection_None)); + Matrix4f eyeOrientation[eye] = Matrix4f(Quatf(eyeRenderPose[eye].Orientation).Inverted()); + Matrix4f eyePose[eye] = Matrix4f::Translation(-Vector3f(eyeRenderPose[eye].Position)); + Matrix4f mvp = projection[eye]*eyeOrientation[eye]*eyePose[eye]; + */ + + // Sample using custom raymath.h (C) -INCOMPLETE- + /* + Matrix projection = MatrixPerspective(eyeRenderDesc[eye].Fov, ((double)screenWidth/(double)screenHeight), 0.01, 1000.0); + Matrix eyeOrientation = QuaternionToMatrix((Quaternion){ -eyeRenderPose[eye].Orientation.x, -eyeRenderPose[eye].Orientation.y, + -eyeRenderPose[eye].Orientation.z, -eyeRenderPose[eye].Orientation.w }); + Matrix eyePose = MatrixTranslate(-eyeRenderPose[eye].Position.x, -eyeRenderPose[eye].Position.y, -eyeRenderPose[eye].Position.z); + Matrix mvp = MatrixMultiply(projection, MatrixMultiply(eyeOrientation, eyePose)); + */ + + // Render everything + // TODO: Pass calculated mvp matrix to default shader to consider projection and orientation! + //DrawRectangleV(position, size, color); + //rlglDraw(); + + UnsetOculusBuffer(eyeRenderBuffer[eye]); + + // Commit changes to the textures so they get picked up frame + ovr_CommitTextureSwapChain(session, eyeRenderBuffer[eye].textureChain); + } + } + + // Set up positional data + ovrViewScaleDesc viewScaleDesc; + viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + viewScaleDesc.HmdToEyeOffset[0] = hmdToEyeOffset[0]; + viewScaleDesc.HmdToEyeOffset[1] = hmdToEyeOffset[1]; + + // Create the main eye layer + ovrLayerEyeFov eyeLayer; + eyeLayer.Header.Type = ovrLayerType_EyeFov; + eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; // Because OpenGL + + for (int eye = 0; eye < 2; eye++) + { + eyeLayer.ColorTexture[eye] = eyeRenderBuffer[eye].textureChain; + eyeLayer.Viewport[eye] = (ovrRecti){ eyeRenderBuffer[eye].width, eyeRenderBuffer[eye].height }; + eyeLayer.Fov[eye] = hmdDesc.DefaultEyeFov[eye]; + eyeLayer.RenderPose[eye] = eyeRenderPose[eye]; + eyeLayer.SensorSampleTime = sensorSampleTime; + } + + // Append all the layers to global list + ovrLayerHeader *layerList = &eyeLayer.Header; + ovrResult result = ovr_SubmitFrame(session, frameIndex, NULL, &layerList, 1); + + // exit the rendering loop if submit returns an error, will retry on ovrError_DisplayLost + if (!OVR_SUCCESS(result)) return 1; + + isVisible = (result == ovrSuccess); + + // Get session status information + ovrSessionStatus sessionStatus; + ovr_GetSessionStatus(session, &sessionStatus); + if (sessionStatus.ShouldQuit) TraceLog(LOG_WARNING, "OVR: Session should quit."); + if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session); + + // Blit mirror texture to back buffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + GLint w = mirrorDesc.Width; + GLint h = mirrorDesc.Height; + glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + glfwSwapBuffers(window); + glfwPollEvents(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + if (mirrorFBO) glDeleteFramebuffers(1, &mirrorFBO); + if (mirrorTexture) ovr_DestroyMirrorTexture(session, mirrorTexture); + for (int eye = 0; eye < 2; eye++) UnloadOculusBuffer(session, eyeRenderBuffer[eye]); + + rlglClose(); + + glfwDestroyWindow(window); + glfwTerminate(); + + ovr_Destroy(session); // Must be called after glfwTerminate() + ovr_Shutdown(); + //-------------------------------------------------------------------------------------- + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definitions +//---------------------------------------------------------------------------------- + +// 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; + desc.SampleCount = 1; + desc.StaticImage = ovrFalse; + + ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &buffer.textureChain); + + int textureCount = 0; + ovr_GetTextureSwapChainLength(session, buffer.textureChain, &textureCount); + + if (OVR_SUCCESS(result)) + { + 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); + } + } + + // Generate framebuffer + glGenFramebuffers(1, &buffer.fboId); + + // Create Depth texture + 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); + + 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) + { + glDeleteTextures(1, &buffer.depthId); + buffer.depthId = 0; + } + + if (buffer.fboId) + { + glDeleteFramebuffers(1, &buffer.fboId); + buffer.fboId = 0; + } +} + +// Set current Oculus buffer +static void SetOculusBuffer(ovrSession session, OculusBuffer buffer) +{ + GLuint currentTexId; + int currentIndex; + + ovr_GetTextureSwapChainCurrentIndex(session, buffer.textureChain, ¤tIndex); + ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, currentIndex, ¤tTexId); + + glBindFramebuffer(GL_FRAMEBUFFER, buffer.fboId); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currentTexId, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, buffer.depthId, 0); + + glViewport(0, 0, buffer.width, buffer.height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_FRAMEBUFFER_SRGB); +} + +// Unset Oculus buffer +static void UnsetOculusBuffer(OculusBuffer buffer) +{ + glBindFramebuffer(GL_FRAMEBUFFER, buffer.fboId); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); +} + +// Draw rectangle using rlgl OpenGL 1.1 style coding (translated to OpenGL 3.3 internally) +static void DrawRectangleV(Vector2 position, Vector2 size, Color color) +{ + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2i(position.x, position.y); + rlVertex2i(position.x, position.y + size.y); + rlVertex2i(position.x + size.x, position.y + size.y); + + rlVertex2i(position.x, position.y); + rlVertex2i(position.x + size.x, position.y + size.y); + rlVertex2i(position.x + size.x, position.y); + rlEnd(); +} + +// Output a trace log message +// NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning +static void TraceLog(int msgType, const char *text, ...) +{ + va_list args; + va_start(args, text); + + switch(msgType) + { + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; + default: break; + } + + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); + + va_end(args); + + //if (msgType == LOG_ERROR) exit(1); +} \ No newline at end of file diff --git a/examples/oculus_glfw_sample/oculus_glfw_sample_new.c b/examples/oculus_glfw_sample/oculus_glfw_sample_new.c deleted file mode 100644 index 4a4689495..000000000 --- a/examples/oculus_glfw_sample/oculus_glfw_sample_new.c +++ /dev/null @@ -1,280 +0,0 @@ - -#include -#include -#include - -#include "glad.h" // Extensions loading library -#include - -#include "OculusSDK/LibOVR/Include/OVR_CAPI_GL.h" // Oculus SDK for OpenGL - -#define FAIL(X) printf(X); - -typedef struct Vector2 { - float x; - float y; -} Vector2; - -typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; -} Matrix; - -// RiftManagerApp class -ovrSession session; -ovrHmdDesc hmdDesc; -ovrGraphicsLuid luid; - -// RiftApp class -GLuint fbo = 0; -GLuint depthBuffer = 0; -ovrTextureSwapChain eyeTexture; - -GLuint mirrorFbo = 0; -ovrMirrorTexture mirrorTexture; -ovrEyeRenderDesc eyeRenderDescs[2]; -Matrix eyeProjections[2]; - -ovrLayerEyeFov eyeLayer; -ovrViewScaleDesc viewScaleDesc; - -Vector2 renderTargetSize; -Vector2 mirrorSize; - -// GlfwApp class -GLFWwindow *window = NULL; -unsigned int frame = 0; - -static void ErrorCallback(int error, const char* description) -{ - fputs(description, stderr); -} - -static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) -{ - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) - { - glfwSetWindowShouldClose(window, GL_TRUE); - } -} - -// Execute our example class -int main() -{ - if (!OVR_SUCCESS(ovr_Initialize(NULL))) FAIL("Failed to initialize the Oculus SDK\n"); - - //result = ExampleApp().run(); // class ExampleApp : public RiftApp : public GlfwApp, public RiftManagerApp - - if (!OVR_SUCCESS(ovr_Create(&session, &luid))) FAIL("Unable to create HMD session\n"); - hmdDesc = ovr_GetHmdDesc(session); - - // RiftApp() constructor - viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; - memset(&eyeLayer, 0, sizeof(ovrLayerEyeFov)); - eyeLayer.Header.Type = ovrLayerType_EyeFov; - eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; - - //ovr::for_each_eye([&](ovrEyeType eye) - 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, 1000.0f, ovrProjection_ClipRangeOpenGL); - //eyeProjections[eye] = ovr::toGlm(ovrPerspectiveProjection); - viewScaleDesc.HmdToEyeOffset[eye] = eyeRenderDescs[eye].HmdToEyeOffset; - - eyeLayer.Fov[eye] = eyeRenderDescs[eye].Fov; - ovrSizei eyeSize = ovr_GetFovTextureSize(session, eye, eyeLayer.Fov[eye], 1.0f); - eyeLayer.Viewport[eye].Size = eyeSize; - eyeLayer.Viewport[eye].Pos.x = renderTargetSize.x; - eyeLayer.Viewport[eye].Pos.y = 0; - - renderTargetSize.y = renderTargetSize.y; // std::max(renderTargetSize.y, (uint32_t)eyeSize.h); - renderTargetSize.x += eyeSize.w; - } - - // Make the on screen window 1/4 the resolution of the render target - mirrorSize = renderTargetSize; - mirrorSize.x /= 2; - mirrorSize.y /= 2; - - // GLFWApp() constructor - if (!glfwInit()) FAIL("Failed to initialize GLFW\n"); // Initialize the GLFW system for creating and positioning windows - glfwSetErrorCallback(ErrorCallback); - - ////preCreate(); - glfwWindowHint(GLFW_DEPTH_BITS, 16); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); - - //***************window = createRenderingTarget(windowSize, windowPosition); //GLFWwindow *createRenderingTarget(uvec2 & size, ivec2 & pos) = 0; //glfw::createWindow(_mirrorSize); - /* - GLFWwindow *createWindow(const uvec2 &size, const ivec2 &position = ivec2(INT_MIN)) - { - GLFWwindow *window = glfwCreateWindow(size.x, size.y, "glfw", NULL, NULL); // size = mirrorSize - - if (!window) FAIL("Unable to create rendering window\n"); - - if ((position.x > INT_MIN) && (position.y > INT_MIN)) // INT_MIN = -32767 // #define INT_MIN (-2147483647 - 1) - { - glfwSetWindowPos(window, position.x, position.y); - } - - return window; - } - */ - - window = glfwCreateWindow(mirrorSize.x, mirrorSize.y, "glfw", NULL, NULL); - - if (!window) FAIL("Unable to create OpenGL window\n"); - - ////postCreate(); - //glfwSetWindowUserPointer(window, this); //// Useful to hack input callbacks - glfwSetKeyCallback(window, KeyCallback); - glfwMakeContextCurrent(window); - - // Initialize the OpenGL extensions - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) FAIL("GLAD failed\n"); - /* - glewExperimental = GL_TRUE; - if (0 != glewInit()) FAIL("Failed to initialize GLEW\n"); - glGetError(); - - if (GLEW_KHR_debug) - { - GLint v; - glGetIntegerv(GL_CONTEXT_FLAGS, &v); - if (v & GL_CONTEXT_FLAG_DEBUG_BIT) glDebugMessageCallback(glDebugCallbackHandler, this); - } - */ - - ////initGl(); - { - // RiftApp::InitGL() -----> - //GlfwApp::initGl(); // virtual - - // Disable the v-sync for buffer swap - glfwSwapInterval(0); - - ovrTextureSwapChainDesc desc = {}; - desc.Type = ovrTexture_2D; - desc.ArraySize = 1; - desc.Width = renderTargetSize.x; - desc.Height = renderTargetSize.y; - desc.MipLevels = 1; - desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; - desc.SampleCount = 1; - desc.StaticImage = ovrFalse; - - ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &eyeTexture); - eyeLayer.ColorTexture[0] = eyeTexture; - - if (!OVR_SUCCESS(result)) FAIL("Failed to create swap textures"); - - int length = 0; - result = ovr_GetTextureSwapChainLength(session, eyeTexture, &length); - - if (!OVR_SUCCESS(result) || !length) FAIL("Unable to count swap chain textures"); - - for (int i = 0; i < length; ++i) - { - GLuint chainTexId; - ovr_GetTextureSwapChainBufferGL(session, eyeTexture, 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); - - // Set up the framebuffer object - glGenFramebuffers(1, &fbo); - glGenRenderbuffers(1, &depthBuffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); - glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, renderTargetSize.x, renderTargetSize.y); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - - ovrMirrorTextureDesc mirrorDesc; - memset(&mirrorDesc, 0, sizeof(mirrorDesc)); - mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; - mirrorDesc.Width = mirrorSize.x; - mirrorDesc.Height = mirrorSize.y; - - if (!OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirrorTexture))) FAIL("Could not create mirror texture"); - - glGenFramebuffers(1, &mirrorFbo); - - // RiftApp::InitGL() <------ - - glClearColor(0.2f, 0.2f, 0.2f, 0.0f); - glEnable(GL_DEPTH_TEST); - ovr_RecenterTrackingOrigin(session); - - // TODO: Init cube scene --> cubeScene = std::shared_ptr(new ColorCubeScene()); - } - - while (!glfwWindowShouldClose(window)) - { - frame++; - glfwPollEvents(); - - //update(); - - //draw(); ------> - ovrPosef eyePoses[2]; - ovr_GetEyePoses(session, frame, ovrTrue, viewScaleDesc.HmdToEyeOffset, eyePoses, &eyeLayer.SensorSampleTime); - - int curIndex; - ovr_GetTextureSwapChainCurrentIndex(session, eyeTexture, &curIndex); - GLuint curTexId; - ovr_GetTextureSwapChainBufferGL(session, eyeTexture, curIndex, &curTexId); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, curTexId, 0); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - for (int eye = 0; eye < 2; eye++) - { - glViewport(eyeLayer.Viewport[eye].Pos.x, eyeLayer.Viewport[eye].Pos.y, - eyeLayer.Viewport[eye].Size.w, eyeLayer.Viewport[eye].Size.h); - eyeLayer.RenderPose[eye] = eyePoses[eye]; - - //renderScene(_eyeProjections[eye], ovr::toGlm(eyePoses[eye])); --> cubeScene->render(projection, glm::inverse(headPose)); - } - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - ovr_CommitTextureSwapChain(session, eyeTexture); - ovrLayerHeader *headerList = &eyeLayer.Header; - - ovr_SubmitFrame(session, frame, &viewScaleDesc, &headerList, 1); - - GLuint mirrorTextureId; - ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &mirrorTextureId); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFbo); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTextureId, 0); - glBlitFramebuffer(0, 0, mirrorSize.x, mirrorSize.y, 0, mirrorSize.y, mirrorSize.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - //draw() <------------- - - glfwSwapBuffers(window); //finishFrame(); - } - - //shutdownGl(); // Delete scene: cubeScene.reset(); - - glfwDestroyWindow(window); - glfwTerminate(); - - ovr_Destroy(session); - ovr_Shutdown(); - - return 0; -} diff --git a/examples/oculus_glfw_sample/raylib_OculusRiftCV1.png b/examples/oculus_glfw_sample/raylib_OculusRiftCV1.png new file mode 100644 index 000000000..37a874892 Binary files /dev/null and b/examples/oculus_glfw_sample/raylib_OculusRiftCV1.png differ diff --git a/examples/oculus_glfw_sample/rlgl.c b/examples/oculus_glfw_sample/rlgl.c index 06efd777e..7129402d8 100644 --- a/examples/oculus_glfw_sample/rlgl.c +++ b/examples/oculus_glfw_sample/rlgl.c @@ -196,23 +196,20 @@ static DrawMode currentDrawMode; static float currentDepth = -1.0f; -// Vertex arrays for lines, triangles and quads +// Default vertex buffers 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; -static Shader currentShader; // By default, defaultShader - -// Vertex Array Objects (VAO) +// Default vertex buffers VAOs (if supported) static GLuint vaoLines, vaoTriangles, vaoQuads; -// Vertex Buffer Objects (VBO) -static GLuint linesBuffer[2]; -static GLuint trianglesBuffer[2]; -static GLuint quadsBuffer[4]; +// Default vertex buffers VBOs +static GLuint linesBuffer[2]; // Lines buffers (position, color) +static GLuint trianglesBuffer[2]; // Triangles buffers (position, color) +static GLuint quadsBuffer[4]; // Quads buffers (position, texcoord, color, index) +// Default buffers draw calls static DrawCall *draws; static int drawsCounter; @@ -221,11 +218,14 @@ static Vector3 *tempBuffer; static int tempBufferCount = 0; static bool useTempBuffer = false; +// Shader Programs +static Shader defaultShader; +static Shader currentShader; // By default, defaultShader + // Flags for supported extensions static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) // 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 @@ -233,8 +233,8 @@ static bool texCompASTCSupported = false; // ASTC texture compression support #endif // Compressed textures support flags -static bool texCompDXTSupported = false; // DDS texture compression support -static bool npotSupported = false; // NPOT textures full support +static bool texCompDXTSupported = false; // DDS texture compression support +static bool npotSupported = false; // NPOT textures full support #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) @@ -254,14 +254,17 @@ unsigned int whiteTexture; // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); + static Shader LoadDefaultShader(void); static void LoadDefaultShaderLocations(Shader *shader); -static void InitializeBuffers(void); -static void InitializeBuffersGPU(void); -static void UpdateBuffers(void); -static char *TextFileRead(char *fn); +static void UnloadDefaultShader(void); -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); +static void LoadDefaultBuffers(void); +static void UpdateDefaultBuffers(void); +static void UnloadDefaultBuffers(void); + +static char *ReadTextFile(const char *fileName); #endif #if defined(GRAPHICS_API_OPENGL_11) @@ -274,20 +277,6 @@ static void TraceLog(int msgType, const char *text, ...); float *MatrixToFloat(Matrix mat); // Converts Matrix to float array #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); -} -#endif - //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations //---------------------------------------------------------------------------------- @@ -919,13 +908,18 @@ void rlglInit(void) // 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, " "); while (extList[numExt] != NULL) @@ -969,10 +963,12 @@ void rlglInit(void) // DDS texture compression support if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || + (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; @@ -1022,12 +1018,9 @@ void rlglInit(void) // Init default Shader (customized for GL 3.3 and ES2) defaultShader = LoadDefaultShader(); - //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok - currentShader = defaultShader; - InitializeBuffers(); // Init vertex arrays - InitializeBuffersGPU(); // Init VBO and VAO + LoadDefaultBuffers(); // Initialize default vertex arrays buffers (lines, triangles, quads) // Init temp vertex buffer, used when transformation required (translate, rotate, scale) tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); @@ -1052,54 +1045,10 @@ void rlglInit(void) 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, vertexShader); - //glDetachShader(defaultShaderProgram, fragmentShader); - //glDeleteShader(vertexShader); // Already deleted on shader compilation - //glDeleteShader(fragmentShader); // Already deleted on sahder compilation - glDeleteProgram(defaultShader.id); - - // 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 + UnloadDefaultShader(); + UnloadDefaultBuffers(); + + // Delete default white texture glDeleteTextures(1, &whiteTexture); TraceLog(INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); @@ -1108,18 +1057,18 @@ void rlglClose(void) } // Drawing batches: triangles, quads, lines -void rlglDraw(void) +void rlglDraw(Matrix mvp) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UpdateBuffers(); + UpdateDefaultBuffers(); if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { glUseProgram(currentShader.id); - Matrix matMVP = MatrixMultiply(modelview, projection); // Create modelview-projection matrix + Matrix mvp2 = MatrixMultiply(modelview, projection); // Create modelview-projection matrix - glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(mvp2)); glUniform1i(currentShader.mapDiffuseLoc, 0); glUniform4f(currentShader.tintColorLoc, 1.0f, 1.0f, 1.0f, 1.0f); } @@ -1348,14 +1297,14 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float ro glBindTexture(GL_TEXTURE_2D, model.material.texDiffuse.id); glUniform1i(model.material.shader.mapDiffuseLoc, 0); // Texture fits in active texture unit 0 - if (model.material.texNormal.id != 0) + if ((model.material.texNormal.id != 0) && (model.material.shader.mapNormalLoc != -1)) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, model.material.texNormal.id); glUniform1i(model.material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 } - if (model.material.texSpecular.id != 0) + if ((model.material.texSpecular.id != 0) && (model.material.shader.mapSpecularLoc != -1)) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, model.material.texSpecular.id); @@ -1844,7 +1793,9 @@ void rlglGenerateMipmaps(Texture2D texture) // NOTE: Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data free(data); -#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id); @@ -2114,8 +2065,8 @@ Shader LoadShader(char *vsFileName, char *fsFileName) #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); + char *vShaderStr = ReadTextFile(vsFileName); + char *fShaderStr = ReadTextFile(fsFileName); if ((vShaderStr != NULL) && (fShaderStr != NULL)) { @@ -2123,17 +2074,13 @@ Shader LoadShader(char *vsFileName, char *fsFileName) // After shader loading, we try to load default location names if (shader.id != 0) LoadDefaultShaderLocations(&shader); - else - { - TraceLog(WARNING, "Custom shader could not be loaded"); - shader = defaultShader; - } // Shader strings must be freed free(vShaderStr); free(fShaderStr); } - else + + if (shader.id == 0) { TraceLog(WARNING, "Custom shader could not be loaded"); shader = defaultShader; @@ -2259,7 +2206,7 @@ void SetCustomShader(Shader shader) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (currentShader.id != shader.id) { - rlglDraw(); + //rlglDraw(); currentShader = shader; } #endif @@ -2365,7 +2312,7 @@ void SetBlendMode(int mode) { if ((blendMode != mode) && (mode < 3)) { - rlglDraw(); + //rlglDraw(); switch (mode) { @@ -2379,18 +2326,6 @@ void SetBlendMode(int mode) } } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void PrintProjectionMatrix(void) -{ - PrintMatrix(projection); -} - -void PrintModelviewMatrix(void) -{ - PrintMatrix(modelview); -} -#endif - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2432,7 +2367,7 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in } } -// Load Shader (Vertex and Fragment) +// Load default shader (Vertex and Fragment) // NOTE: This shader program is used for batch buffers (lines, triangles, quads) static Shader LoadDefaultShader(void) { @@ -2492,7 +2427,7 @@ static Shader LoadDefaultShader(void) 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); - LoadDefaultShaderLocations(&shader); + if (shader.id != 0) LoadDefaultShaderLocations(&shader); return shader; } @@ -2517,43 +2452,24 @@ static void LoadDefaultShaderLocations(Shader *shader) shader->mapSpecularLoc = glGetUniformLocation(shader->id, "texture2"); } -// Read text file -// NOTE: text chars array should be freed manually -static char *TextFileRead(char *fileName) +// Unload default shader +static void UnloadDefaultShader(void) { - FILE *textFile; - char *text = NULL; + glUseProgram(0); - int count = 0; - - if (fileName != NULL) - { - textFile = fopen(fileName,"rt"); - - if (textFile != NULL) - { - fseek(textFile, 0, SEEK_END); - count = ftell(textFile); - rewind(textFile); - - if (count > 0) - { - text = (char *)malloc(sizeof(char)*(count + 1)); - count = fread(text, sizeof(char), count, textFile); - text[count] = '\0'; - } - - fclose(textFile); - } - else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); - } - - return text; + //glDetachShader(defaultShaderProgram, vertexShader); + //glDetachShader(defaultShaderProgram, fragmentShader); + //glDeleteShader(vertexShader); // Already deleted on shader compilation + //glDeleteShader(fragmentShader); // Already deleted on sahder 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) { + // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) + //-------------------------------------------------------------------------------------------- + // Initialize lines 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 @@ -2607,13 +2523,14 @@ static void InitializeBuffers(void) quads.tcCounter = 0; quads.cCounter = 0; - TraceLog(INFO, "CPU buffers (lines, triangles, quads) initialized successfully"); -} - -// Initialize Vertex Array Objects (Contain VBO) -// NOTE: lines, triangles and quads buffers use currentShader -static void InitializeBuffersGPU(void) -{ + TraceLog(INFO, "Default buffers initialized successfully in CPU (lines, triangles, quads)"); + //-------------------------------------------------------------------------------------------- + + // [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 @@ -2636,10 +2553,10 @@ static void InitializeBuffersGPU(void) 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 (lines) VAO initialized successfully", vaoLines); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers (lines) VBOs initialized successfully", linesBuffer[0], linesBuffer[1]); + // Upload and link triangles vertex buffers if (vaoSupported) { // Initialize Triangles VAO @@ -2661,10 +2578,10 @@ static void InitializeBuffersGPU(void) 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 (triangles) VAO initialized successfully", vaoTriangles); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers (triangles) VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]); + // Upload and link quads vertex buffers if (vaoSupported) { // Initialize Quads VAO @@ -2699,18 +2616,20 @@ static void InitializeBuffersGPU(void) 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 (quads) VAO initialized successfully", vaoQuads); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers (quads) VBOs initialized successfully", quadsBuffer[0], quadsBuffer[1], quadsBuffer[2], quadsBuffer[3]); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- } -// Update VBOs with vertex array data +// Update default 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 @@ -2726,8 +2645,8 @@ static void UpdateBuffers(void) //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 @@ -2743,8 +2662,8 @@ static void UpdateBuffers(void) //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 @@ -2766,7 +2685,7 @@ static void UpdateBuffers(void) 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); } @@ -2775,6 +2694,83 @@ static void UpdateBuffers(void) // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); } + +// Unload default 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, &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 from GPU (VRAM) + glDeleteVertexArrays(1, &vaoLines); + glDeleteVertexArrays(1, &vaoTriangles); + glDeleteVertexArrays(1, &vaoQuads); + } + + // 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); +} + +// Read text data from file +// NOTE: text chars array should be freed manually +static char *ReadTextFile(const char *fileName) +{ + FILE *textFile; + char *text = NULL; + + int count = 0; + + if (fileName != NULL) + { + textFile = fopen(fileName,"rt"); + + if (textFile != NULL) + { + fseek(textFile, 0, SEEK_END); + count = ftell(textFile); + rewind(textFile); + + if (count > 0) + { + text = (char *)malloc(sizeof(char)*(count + 1)); + count = fread(text, sizeof(char), count, textFile); + text[count] = '\0'; + } + + fclose(textFile); + } + else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); + } + + return text; +} #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_11) @@ -2905,7 +2901,6 @@ static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) #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, ...) diff --git a/examples/oculus_glfw_sample/rlgl.h b/examples/oculus_glfw_sample/rlgl.h index 714961e11..99427929a 100644 --- a/examples/oculus_glfw_sample/rlgl.h +++ b/examples/oculus_glfw_sample/rlgl.h @@ -273,7 +273,7 @@ int rlGetVersion(void); // Returns current OpenGL versio //------------------------------------------------------------------------------------ void rlglInit(void); // Initialize rlgl (shaders, VAO, VBO...) void rlglClose(void); // De-init rlgl -void rlglDraw(void); // Draw VAO/VBO +void rlglDraw(Matrix mvp); // Draw VAO/VBO void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load texture in GPU @@ -292,11 +292,6 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void PrintProjectionMatrix(void); // DEBUG: Print projection matrix -void PrintModelviewMatrix(void); // DEBUG: Print modelview matrix -#endif - #if defined(RLGL_STANDALONE) //------------------------------------------------------------------------------------ // Shaders System Functions (Module: rlgl) @@ -309,13 +304,10 @@ void SetCustomShader(Shader shader); // Set custo void SetDefaultShader(void); // Set default shader to be used in batch draw void SetModelShader(Model *model, Shader shader); // Link a shader to a model -int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location -void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float) -void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int) -void SetShaderMapDiffuse(Shader *shader, Texture2D texture); // Default diffuse shader map texture assignment -void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture); // Normal map texture shader assignment -void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture); // Specular map texture shader assignment -void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textureUnit); // TODO: Generic shader map assignment +int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float) +void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int) +void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) #endif