From ba60918eaae142598fc84ee7e2af06b3ddc26ba1 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 4 May 2016 20:25:32 +0200 Subject: [PATCH 01/11] Updated Oculus sample Now GLFW3 windows/context creation works ok and a sample red rectangle has been drawn using rlgl. Next step is working in tracking position/orientation maths and try to get a simple 3d scene... --- .../oculus_glfw_sample/oculus_glfw_sample.c | 604 +++++++++--------- .../oculus_glfw_sample.old.c | 498 +++++++++++++++ .../oculus_glfw_sample_new.c | 280 -------- .../raylib_OculusRiftCV1.png | Bin 0 -> 218472 bytes examples/oculus_glfw_sample/rlgl.c | 339 +++++----- examples/oculus_glfw_sample/rlgl.h | 18 +- 6 files changed, 975 insertions(+), 764 deletions(-) create mode 100644 examples/oculus_glfw_sample/oculus_glfw_sample.old.c delete mode 100644 examples/oculus_glfw_sample/oculus_glfw_sample_new.c create mode 100644 examples/oculus_glfw_sample/raylib_OculusRiftCV1.png 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 0000000000000000000000000000000000000000..37a87489243cfe3a28c2b7d119f7390845180794 GIT binary patch literal 218472 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
0}XlmAg3e|81jV|X%i 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 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 From ec72a8868e61507a3dfdc83bfe30c7110fc3051f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 7 May 2016 18:04:22 +0200 Subject: [PATCH 02/11] Comment tweak --- src/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core.c b/src/core.c index 669010f95..54d42f837 100644 --- a/src/core.c +++ b/src/core.c @@ -1448,6 +1448,7 @@ static void InitDisplay(int width, int height) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! // Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); } if (fullscreen) From 7ab008878afa202b4f2e579567be7a7d87242661 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 7 May 2016 18:07:15 +0200 Subject: [PATCH 03/11] Library redesign to accomodate materials system --- src/models.c | 164 ++++++++-- src/raylib.h | 31 +- src/rlgl.c | 845 +++++++++++++++++++++++++-------------------------- src/rlgl.h | 15 +- 4 files changed, 569 insertions(+), 486 deletions(-) diff --git a/src/models.c b/src/models.c index 0bb2b8d69..9b139120b 100644 --- a/src/models.c +++ b/src/models.c @@ -55,7 +55,9 @@ extern unsigned int whiteTexture; //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static Mesh LoadOBJ(const char *fileName); +static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data +static Material LoadMTL(const char *fileName); // Load MTL material data + static Mesh GenMeshHeightmap(Image image, Vector3 size); static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); @@ -542,24 +544,19 @@ void DrawGizmo(Vector3 position) Model LoadModel(const char *fileName) { Model model = { 0 }; - Mesh mesh = { 0 }; - // NOTE: Initialize default data for model in case loading fails, maybe a cube? + // TODO: Initialize default data for model in case loading fails, maybe a cube? - if (strcmp(GetExtension(fileName),"obj") == 0) mesh = LoadOBJ(fileName); + if (strcmp(GetExtension(fileName),"obj") == 0) model.mesh = LoadOBJ(fileName); else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); - // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct - - if (mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded"); + if (model.mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded"); else { - // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel() - model = rlglLoadModel(mesh); // Upload vertex data to GPU - - // NOTE: Now that vertex data is uploaded to GPU VRAM, we can free arrays from CPU RAM - // We don't need CPU vertex data on OpenGL 3.3 or ES2... for static meshes... - // ...but we could keep CPU vertex data in case we need to update the mesh + rlglLoadMesh(&model.mesh); // Upload vertex data to GPU + + model.transform = MatrixIdentity(); + model.material = LoadDefaultMaterial(); } return model; @@ -568,12 +565,12 @@ Model LoadModel(const char *fileName) // Load a 3d model (from vertex data) Model LoadModelEx(Mesh data) { - Model model; + Model model = { 0 }; - // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel() - model = rlglLoadModel(data); // Upload vertex data to GPU + rlglLoadMesh(&data); // Upload vertex data to GPU - // NOTE: Vertex data is managed externally, must be deallocated manually + model.transform = MatrixIdentity(); + model.material = LoadDefaultMaterial(); return model; } @@ -582,8 +579,14 @@ Model LoadModelEx(Mesh data) // NOTE: model map size is defined in generic units Model LoadHeightmap(Image heightmap, Vector3 size) { - Mesh mesh = GenMeshHeightmap(heightmap, size); - Model model = rlglLoadModel(mesh); + Model model = { 0 }; + + model.mesh = GenMeshHeightmap(heightmap, size); + + rlglLoadMesh(&model.mesh); + + model.transform = MatrixIdentity(); + model.material = LoadDefaultMaterial(); return model; } @@ -591,8 +594,14 @@ Model LoadHeightmap(Image heightmap, Vector3 size) // Load a map image as a 3d model (cubes based) Model LoadCubicmap(Image cubicmap) { - Mesh mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0, 1.0, 1.5f }); - Model model = rlglLoadModel(mesh); + Model model = { 0 }; + + model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0, 1.0, 1.5f }); + + rlglLoadMesh(&model.mesh); + + model.transform = MatrixIdentity(); + model.material = LoadDefaultMaterial(); return model; } @@ -613,13 +622,44 @@ void UnloadModel(Model model) rlDeleteBuffers(model.mesh.vboId[0]); // vertex rlDeleteBuffers(model.mesh.vboId[1]); // texcoords rlDeleteBuffers(model.mesh.vboId[2]); // normals - //rlDeleteBuffers(model.mesh.vboId[3]); // texcoords2 (NOT USED) + //rlDeleteBuffers(model.mesh.vboId[3]); // colors (NOT USED) //rlDeleteBuffers(model.mesh.vboId[4]); // tangents (NOT USED) - //rlDeleteBuffers(model.mesh.vboId[5]); // colors (NOT USED) + //rlDeleteBuffers(model.mesh.vboId[5]); // texcoords2 (NOT USED) rlDeleteVertexArrays(model.mesh.vaoId); } +// Load material data (from file) +Material LoadMaterial(const char *fileName) +{ + Material material = { 0 }; + + if (strcmp(GetExtension(fileName),"mtl") == 0) material = LoadMTL(fileName); + else TraceLog(WARNING, "[%s] Material extension not recognized, it can't be loaded", fileName); + + return material; +} + +// Load default material (uses default models shader) +Material LoadDefaultMaterial(void) +{ + Material material = { 0 }; + + material.shader = GetDefaultShader(); + material.texDiffuse = GetDefaultTexture(); // White texture (1x1 pixel) + //material.texNormal; // NOTE: By default, not set + //material.texSpecular; // NOTE: By default, not set + + material.colDiffuse = WHITE; // Diffuse color + material.colAmbient = WHITE; // Ambient color + material.colSpecular = WHITE; // Specular color + + material.glossiness = 100.0f; // Glossiness level + material.normalDepth = 1.0f; // Normal map depth + + return material; +} + // Link a texture to a model void SetModelTexture(Model *model, Texture2D texture) { @@ -1100,31 +1140,59 @@ void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; - + DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint); } // Draw a model with extended parameters void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) { - // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() - rlglDrawModel(model, position, rotationAxis, rotationAngle, scale, tint, false); + // 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); + + // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) + //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates + + model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); + model.material.colDiffuse = tint; + + rlglDrawEx(model.mesh, model.material, model.transform, false); } // Draw a model wires (with texture if set) -void DrawModelWires(Model model, Vector3 position, float scale, Color color) +void DrawModelWires(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; - rlglDrawModel(model, position, rotationAxis, 0.0f, vScale, color, true); + // Calculate transformation matrix from function parameters + // Get transform matrix (rotation -> scale -> translation) + Matrix matRotation = MatrixRotate(rotationAxis, 0.0f); + Matrix matScale = MatrixScale(vScale.x, vScale.y, vScale.z); + Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); + + model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); + model.material.colDiffuse = tint; + + rlglDrawEx(model.mesh, model.material, model.transform, true); } // Draw a model wires (with texture if set) with extended parameters void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) { - // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() - rlglDrawModel(model, position, rotationAxis, rotationAngle, scale, tint, true); + // 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); + + model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); + model.material.colDiffuse = tint; + + rlglDrawEx(model.mesh, model.material, model.transform, true); } // Draw a billboard @@ -1856,3 +1924,39 @@ static Mesh LoadOBJ(const char *fileName) return mesh; } + +// Load MTL material data +static Material LoadMTL(const char *fileName) +{ + Material material = { 0 }; + + // TODO: Load mtl file + + char dataType; + char comments[200]; + + FILE *mtlFile; + + mtlFile = fopen(fileName, "rt"); + + if (mtlFile == NULL) + { + TraceLog(WARNING, "[%s] MTL file could not be opened", fileName); + return material; + } + + // First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles + // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) + // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) + while(!feof(mtlFile)) + { + fscanf(mtlFile, "%c", &dataType); + } + + fclose(mtlFile); + + // NOTE: At this point we have all material data + TraceLog(INFO, "[%s] Material loaded successfully", fileName); + + return material; +} diff --git a/src/raylib.h b/src/raylib.h index 8af7d2fb8..c88a60f45 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -387,10 +387,10 @@ typedef struct Shader { unsigned int id; // Shader program id // Variable attributes locations - int vertexLoc; // Vertex attribute location point (vertex shader) - int texcoordLoc; // Texcoord attribute location point (vertex shader) - int normalLoc; // Normal attribute location point (vertex shader) - int colorLoc; // Color attibute location point (vertex shader) + int vertexLoc; // Vertex attribute location point (default-location = 0) + int texcoordLoc; // Texcoord attribute location point (default-location = 1) + int normalLoc; // Normal attribute location point (default-location = 2) + int colorLoc; // Color attibute location point (default-location = 3) // Uniform locations int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) @@ -801,17 +801,20 @@ void DrawGizmo(Vector3 position); //------------------------------------------------------------------------------------ // Model 3d Loading and Drawing Functions (Module: models) //------------------------------------------------------------------------------------ -Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) -Model LoadModelEx(Mesh data); // Load a 3d model (from mesh data) -//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) -Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model -Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) -void UnloadModel(Model model); // Unload 3d model from memory -void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model +Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) +Model LoadModelEx(Mesh data); // Load a 3d model (from mesh data) +//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) +Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model +Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) +void UnloadModel(Model model); // Unload 3d model from memory +void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model + +Material LoadMaterial(const char *fileName); // Load material data (from file) +Material LoadDefaultMaterial(void); // Load default material (uses default models shader) void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters -void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires (with texture if set) +void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) @@ -832,11 +835,11 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p // NOTE: This functions are useless when using OpenGL 1.1 //------------------------------------------------------------------------------------ Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations -unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shaders strings and return program id void UnloadShader(Shader shader); // Unload a custom shader from memory void SetDefaultShader(void); // Set default shader to be used in batch draw void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw -void SetModelShader(Model *model, Shader shader); // Link a shader to a model +Shader GetDefaultShader(void); // Get default shader +Texture2D GetDefaultTexture(void); // Get default texture 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) diff --git a/src/rlgl.c b/src/rlgl.c index 9112e47e2..02649e305 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -255,14 +255,16 @@ unsigned int whiteTexture; //---------------------------------------------------------------------------------- #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 unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id -static Shader LoadDefaultShader(void); -static void LoadDefaultShaderLocations(Shader *shader); -static void UnloadDefaultShader(void); +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); -static void UpdateDefaultBuffers(void); -static void UnloadDefaultBuffers(void); +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(void); // Draw default internal buffers vertex data +static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU static char *ReadTextFile(const char *fileName); #endif @@ -1061,167 +1063,12 @@ void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UpdateDefaultBuffers(); - - 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); - glUniform4f(currentShader.tintColorLoc, 1.0f, 1.0f, 1.0f, 1.0f); - } - - // 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; + DrawDefaultBuffers(); #endif } -// 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) +// Draw a 3d mesh with material and transform +void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) { #if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glPolygonMode() not available on OpenGL ES @@ -1230,27 +1077,21 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float ro #if defined(GRAPHICS_API_OPENGL_11) glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, model.material.texDiffuse.id); + 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 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) + glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array + glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array + //glColorPointer(4, GL_UNSIGNED_BYTE, 0, 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(); + rlMultMatrixf(MatrixToFloat(transform)); + + glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array @@ -1261,97 +1102,83 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float ro #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUseProgram(model.material.shader.id); + glUseProgram(material.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 + Matrix matModelView = MatrixMultiply(transform, 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.material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - // Apply color tinting to model + // Apply color tinting (material.colDiffuse) // 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.material.shader.tintColorLoc, 1, vColor); + float vColor[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; + glUniform4fv(material.shader.tintColorLoc, 1, vColor); // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, model.material.texDiffuse.id); - glUniform1i(model.material.shader.mapDiffuseLoc, 0); // Texture fits in active texture unit 0 + glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + glUniform1i(material.shader.mapDiffuseLoc, 0); // Texture fits in active texture unit 0 - if ((model.material.texNormal.id != 0) && (model.material.shader.mapNormalLoc != -1)) + if ((material.texNormal.id != 0) && (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 + glBindTexture(GL_TEXTURE_2D, material.texNormal.id); + glUniform1i(material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 } - if ((model.material.texSpecular.id != 0) && (model.material.shader.mapSpecularLoc != -1)) + if ((material.texSpecular.id != 0) && (material.shader.mapSpecularLoc != -1)) { glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, model.material.texSpecular.id); - glUniform1i(model.material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 + glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); + glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 } if (vaoSupported) { - glBindVertexArray(model.mesh.vaoId); + glBindVertexArray(mesh.vaoId); } else { - // Bind model VBO data: vertex position - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[0]); - glVertexAttribPointer(model.material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.vertexLoc); + // 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 model VBO data: vertex texcoords - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[1]); - glVertexAttribPointer(model.material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.texcoordLoc); + // 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 model VBO data: vertex normals (if available) - if (model.material.shader.normalLoc != -1) + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.normalLoc != -1) { - glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[2]); - glVertexAttribPointer(model.material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.normalLoc); + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.normalLoc); } - // TODO: Bind model VBO data: colors, tangents, texcoords2 (if available) + // TODO: Bind mesh VBO data: colors, tangents, texcoords2 (if available) } // Draw call! - glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); + glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); - //glDisableVertexAttribArray(model.shader.vertexLoc); - //glDisableVertexAttribArray(model.shader.texcoordLoc); - //if (model.shader.normalLoc != -1) glDisableVertexAttribArray(model.shader.normalLoc); - - if (model.material.texNormal.id != 0) + if (material.texNormal.id != 0) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); } - if (model.material.texSpecular.id != 0) + if (material.texSpecular.id != 0) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); @@ -1808,95 +1635,75 @@ void rlglGenerateMipmaps(Texture2D texture) 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) { - Model model; - - model.mesh = mesh; - model.mesh.vaoId = 0; // Vertex Array Object - model.mesh.vboId[0] = 0; // Vertex positions VBO - model.mesh.vboId[1] = 0; // Vertex texcoords VBO - model.mesh.vboId[2] = 0; // Vertex normals VBO + 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 color VBO + mesh->vboId[4] = 0; // Vertex tangent VBO + mesh->vboId[5] = 0; // Vertex texcoord2 VBO // TODO: Consider attributes: color, texcoords2, tangents (if available) - model.transform = MatrixIdentity(); - -#if defined(GRAPHICS_API_OPENGL_11) - model.material.texDiffuse.id = 0; // No texture required - model.material.shader.id = 0; // No shader used - -#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - model.material.shader = defaultShader; // Default model shader - - model.material.texDiffuse.id = whiteTexture; // Default whiteTexture - model.material.texDiffuse.width = 1; // Default whiteTexture width - model.material.texDiffuse.height = 1; // Default whiteTexture height - model.material.texDiffuse.format = UNCOMPRESSED_R8G8B8A8; // Default whiteTexture format - - model.material.texNormal.id = 0; // By default, no normal texture - model.material.texSpecular.id = 0; // By default, no specular texture - - // TODO: Fill default material properties (color, glossiness...) - - GLuint vaoModel = 0; // Vertex Array Objects (VAO) - GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + GLuint vaoId = 0; // Vertex Array Objects (VAO) + GLuint vboId[3]; // 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); + glGenBuffers(3, vboId); - // NOTE: Default shader is assigned to model, so vbo buffers are properly linked to vertex attribs - // If model shader is changed, vbo buffers must be re-assigned to new location points (previously loaded) - - // 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.material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.vertexLoc); - - // 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.material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.texcoordLoc); - - // 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.material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(model.material.shader.normalLoc); + // NOTE: Attributes must be uploaded considering default locations points - glVertexAttrib4f(model.material.shader.colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); // Color vertex attribute set to default: WHITE - glDisableVertexAttribArray(model.material.shader.colorLoc); + // Enable vertex attributes: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(0); + + // Enable vertex attributes: texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(1); + + // Enable vertex attributes: normals (shader-location = 2) + glBindBuffer(GL_ARRAY_BUFFER, vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, GL_STATIC_DRAW); + glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(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) + glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); // Color vertex attribute set to default: WHITE + glDisableVertexAttribArray(3); + + mesh->vboId[0] = vboId[0]; // Vertex position VBO + mesh->vboId[1] = vboId[1]; // Texcoords VBO + mesh->vboId[2] = vboId[2]; // Normals 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, "[VBO ID %i][VBO ID %i][VBO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vboId[0], mesh->vboId[1], mesh->vboId[2]); } #endif - - return model; } // Read screen pixel data (color buffer) @@ -2090,106 +1897,6 @@ Shader LoadShader(char *vsFileName, char *fsFileName) return shader; } -// Load custom shader strings and return program id -unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr) -{ - 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); - - 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); - - char log[maxLength]; - - glGetShaderInfoLog(vertexShader, maxLength, &length, log); - - TraceLog(INFO, "%s", log); - } - 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); - - int maxLength = 0; - int length; - - glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); - - char log[maxLength]; - - glGetShaderInfoLog(fragmentShader, maxLength, &length, log); - - TraceLog(INFO, "%s", log); - } - 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) - { - 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; - } - else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); -#endif - return program; -} - // Unload a custom shader from memory void UnloadShader(Shader shader) { @@ -2220,34 +1927,10 @@ void SetDefaultShader(void) #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->material.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 - -#elif (GRAPHICS_API_OPENGL_11) - TraceLog(WARNING, "Shaders not supported on OpenGL 1.1"); -#endif + return defaultShader; } // Get shader uniform location @@ -2367,7 +2050,131 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in } } -// Load default shader (Vertex and Fragment) +Texture2D GetDefaultTexture(void) +{ + Texture2D texture; + + texture.id = whiteTexture; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = UNCOMPRESSED_R8G8B8A8; + + return texture; +} + +// Load custom shader strings and return program id +static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr) +{ + 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); + + 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); + + char log[maxLength]; + + glGetShaderInfoLog(vertexShader, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + } + 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); + + int maxLength = 0; + int length; + + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); + + char log[maxLength]; + + glGetShaderInfoLog(fragmentShader, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + } + 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, "vertexPosition"); + glBindAttribLocation(program, 1, "vertexTexCoord"); + glBindAttribLocation(program, 2, "vertexNormal"); + glBindAttribLocation(program, 3, "vertexColor"); + glBindAttribLocation(program, 4, "vertexTangent"); + glBindAttribLocation(program, 5, "vertexTexCoord2"); + + // 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); + + char log[maxLength]; + + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + + glDeleteProgram(program); + + program = 0; + } + else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); +#endif + return program; +} + + +// 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) { @@ -2436,6 +2243,12 @@ static Shader LoadDefaultShader(void) // NOTE: If any location is not found, loc point becomes -1 static void LoadDefaultShaderLocations(Shader *shader) { + // 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 + // Get handles to GLSL input attibute locations shader->vertexLoc = glGetAttribLocation(shader->id, "vertexPosition"); shader->texcoordLoc = glGetAttribLocation(shader->id, "vertexTexCoord"); @@ -2470,7 +2283,7 @@ 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 - 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 @@ -2480,7 +2293,7 @@ static void LoadDefaultBuffers(void) lines.vCounter = 0; lines.cCounter = 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 @@ -2490,7 +2303,7 @@ static void LoadDefaultBuffers(void) triangles.vCounter = 0; triangles.cCounter = 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 @@ -2523,7 +2336,7 @@ static void LoadDefaultBuffers(void) quads.tcCounter = 0; quads.cCounter = 0; - TraceLog(INFO, "Default buffers initialized successfully in CPU (lines, triangles, quads)"); + TraceLog(INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); //-------------------------------------------------------------------------------------------- // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) @@ -2541,20 +2354,21 @@ static void LoadDefaultBuffers(void) // Create buffers for our vertex data glGenBuffers(2, linesBuffer); - // Lines - Vertex positions buffer binding and attributes enable + // Lines - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[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 + // Vertex color buffer (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[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] 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]); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", vaoLines); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", linesBuffer[0], linesBuffer[1]); // Upload and link triangles vertex buffers if (vaoSupported) @@ -2567,19 +2381,21 @@ static void LoadDefaultBuffers(void) // Create buffers for our vertex data glGenBuffers(2, trianglesBuffer); - // Enable vertex attributes + // Triangles - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[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); + // Vertex color buffer (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[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] 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]); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", vaoTriangles); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully(triangles)", trianglesBuffer[0], trianglesBuffer[1]); // Upload and link quads vertex buffers if (vaoSupported) @@ -2592,17 +2408,20 @@ static void LoadDefaultBuffers(void) // Create buffers for our vertex data glGenBuffers(4, quadsBuffer); - // Enable vertex attributes + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[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); + // Vertex texcoord buffer (shader-location = 1) glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[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); + // Vertex color buffer (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(currentShader.colorLoc); @@ -2616,15 +2435,15 @@ static void LoadDefaultBuffers(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] 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]); + if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", vaoQuads); + else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quadsBuffer[0], quadsBuffer[1], quadsBuffer[2], quadsBuffer[3]); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); //-------------------------------------------------------------------------------------------- } -// Update default buffers (VAOs/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 re-update GPU arrays (change flag required) static void UpdateDefaultBuffers(void) @@ -2695,7 +2514,165 @@ static void UpdateDefaultBuffers(void) if (vaoSupported) glBindVertexArray(0); } -// Unload default buffers vertex data from CPU and GPU +// Draw default internal buffers vertex data +// NOTE: We draw in this order: lines, triangles, quads +static void DrawDefaultBuffers(void) +{ + // 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)); + glUniform1i(currentShader.mapDiffuseLoc, 0); + glUniform4f(currentShader.tintColorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + } + + // Draw lines buffers + if (lines.vCounter > 0) + { + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(vaoLines); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: color (shader-location = 3) + 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); + } + + // Draw triangles buffers + if (triangles.vCounter > 0) + { + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(vaoTriangles); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: color (shader-location = 3) + 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); + } + + // Draw quads buffers + if (quads.vCounter > 0) + { + int quadsCount = 0; + int numIndicesToProcess = 0; + int indicesOffset = 0; + + if (vaoSupported) + { + glBindVertexArray(vaoQuads); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); + glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.texcoordLoc); + + // Bind vertex attrib: color (shader-location = 3) + 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; +} + +// Unload default internal buffers vertex data from CPU and GPU static void UnloadDefaultBuffers(void) { // Unbind everything diff --git a/src/rlgl.h b/src/rlgl.h index cd8e6d1db..afc2ab96f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -280,29 +280,28 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments) void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture - -// NOTE: There is a set of shader related functions that are available to end user, -// to avoid creating function wrappers through core module, they have been directly declared in raylib.h - -Model rlglLoadModel(Mesh mesh); // Upload vertex data into GPU and provided VAO/VBO ids -void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color color, bool wires); +void rlglLoadMesh(Mesh *mesh); // Upload vertex data into GPU and provided VAO/VBO ids +void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires); Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data +// NOTE: There is a set of shader related functions that are available to end user, +// to avoid creating function wrappers through core module, they have been directly declared in raylib.h + #if defined(RLGL_STANDALONE) //------------------------------------------------------------------------------------ // Shaders System Functions (Module: rlgl) // NOTE: This functions are useless when using OpenGL 1.1 //------------------------------------------------------------------------------------ Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations -unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id void UnloadShader(Shader shader); // Unload a custom shader from memory void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw void SetDefaultShader(void); // Set default shader to be used in batch draw -void SetModelShader(Model *model, Shader shader); // Link a shader to a model +Shader GetDefaultShader(void); // Get default shader +Texture2D GetDefaultTexture(void); // Get default texture 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) From eeb151586f12f8694cccaecf12d92af7842338b5 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 7 May 2016 18:28:40 +0200 Subject: [PATCH 04/11] Corrected issues with OpenGL 1.1 backend --- src/rlgl.c | 58 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/rlgl.c b/src/rlgl.c index 02649e305..462ccdec4 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1080,22 +1080,24 @@ void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) 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 - glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + 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, mesh.vertices); // Pointer to vertex coords array - glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array - glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals 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 + glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array //glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array (NOT USED) - rlMultMatrixf(MatrixToFloat(transform)); - - glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); + glDrawArrays(GL_TRIANGLES, 0, 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 + 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); @@ -1865,6 +1867,20 @@ void *rlglReadTexturePixels(Texture2D texture) // NOTE: Those functions are exposed directly to the user in raylib.h //---------------------------------------------------------------------------------- +// Get default internal texture (white texture) +Texture2D GetDefaultTexture(void) +{ + Texture2D texture; + + texture.id = whiteTexture; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = UNCOMPRESSED_R8G8B8A8; + + return texture; +} + // Load a custom shader and bind default locations Shader LoadShader(char *vsFileName, char *fsFileName) { @@ -1930,7 +1946,12 @@ void SetDefaultShader(void) // Get default shader Shader GetDefaultShader(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) return defaultShader; +#else + Shader shader = { 0 }; + return shader; +#endif } // Get shader uniform location @@ -2050,19 +2071,6 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in } } -Texture2D GetDefaultTexture(void) -{ - Texture2D texture; - - texture.id = whiteTexture; - texture.width = 1; - texture.height = 1; - texture.mipmaps = 1; - texture.format = UNCOMPRESSED_R8G8B8A8; - - return texture; -} - // Load custom shader strings and return program id static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr) { From 29761c25190dee31ca3985ae4af602f3b3b53db3 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 7 May 2016 18:29:04 +0200 Subject: [PATCH 05/11] Testing new material usage --- examples/shaders_model_shader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/shaders_model_shader.c b/examples/shaders_model_shader.c index b302f631b..a1e00671a 100644 --- a/examples/shaders_model_shader.c +++ b/examples/shaders_model_shader.c @@ -37,8 +37,8 @@ int main() Shader shader = LoadShader("resources/shaders/glsl330/base.vs", "resources/shaders/glsl330/grayscale.fs"); // Load model shader - SetModelShader(&dwarf, shader); // Set shader effect to 3d model - SetModelTexture(&dwarf, texture); // Bind texture to model + dwarf.material.shader = shader; // Set shader effect to 3d model + dwarf.material.texDiffuse = texture; // Bind texture to model Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position From 0bcb873cbb3758d67b1d263fafb6be818ddbf067 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 8 May 2016 15:24:02 +0200 Subject: [PATCH 06/11] Improved mesh support Depending on mesh data, it can be loaded and default vertex attribute location points are set, including colors, tangents and texcoords2 --- src/models.c | 120 ++++++++++++++++++++++++++++++++++++++++----------- src/raylib.h | 6 +-- src/rlgl.c | 90 +++++++++++++++++++++++++++++++------- 3 files changed, 171 insertions(+), 45 deletions(-) diff --git a/src/models.c b/src/models.c index 9b139120b..4aff72163 100644 --- a/src/models.c +++ b/src/models.c @@ -575,6 +575,89 @@ Model LoadModelEx(Mesh data) return model; } +// Load a 3d model from rRES file (raylib Resource) +Model LoadModelFromRES(const char *rresName, int resId) +{ + Model model = { 0 }; + bool found = false; + + char id[4]; // rRES file identifier + unsigned char version; // rRES file version and subversion + char useless; // rRES header reserved data + short numRes; + + ResInfoHeader infoHeader; + + FILE *rresFile = fopen(rresName, "rb"); + + if (rresFile == NULL) + { + TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); + } + else + { + // Read rres file (basic file check - id) + fread(&id[0], sizeof(char), 1, rresFile); + fread(&id[1], sizeof(char), 1, rresFile); + fread(&id[2], sizeof(char), 1, rresFile); + fread(&id[3], sizeof(char), 1, rresFile); + fread(&version, sizeof(char), 1, rresFile); + fread(&useless, sizeof(char), 1, rresFile); + + if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) + { + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); + } + else + { + // Read number of resources embedded + fread(&numRes, sizeof(short), 1, rresFile); + + for (int i = 0; i < numRes; i++) + { + fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); + + if (infoHeader.id == resId) + { + found = true; + + // Check data is of valid MODEL type + if (infoHeader.type == 8) + { + // TODO: Load model data + } + else + { + TraceLog(WARNING, "[%s] Required resource do not seem to be a valid MODEL resource", rresName); + } + } + else + { + // Depending on type, skip the right amount of parameters + switch (infoHeader.type) + { + case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters + case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters + case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) + case 3: break; // TEXT: No parameters + case 4: break; // RAW: No parameters + default: break; + } + + // Jump DATA to read next infoHeader + fseek(rresFile, infoHeader.size, SEEK_CUR); + } + } + } + + fclose(rresFile); + } + + if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); + + return model; +} + // Load a heightmap image as a 3d model // NOTE: model map size is defined in generic units Model LoadHeightmap(Image heightmap, Vector3 size) @@ -613,18 +696,18 @@ void UnloadModel(Model model) free(model.mesh.vertices); free(model.mesh.texcoords); free(model.mesh.normals); - free(model.mesh.colors); - //if (model.mesh.texcoords2 != NULL) free(model.mesh.texcoords2); // Not used - //if (model.mesh.tangents != NULL) free(model.mesh.tangents); // Not used + if (model.mesh.colors != NULL) free(model.mesh.colors); + if (model.mesh.tangents != NULL) free(model.mesh.tangents); + if (model.mesh.texcoords2 != NULL) free(model.mesh.texcoords2); TraceLog(INFO, "Unloaded model data from RAM (CPU)"); rlDeleteBuffers(model.mesh.vboId[0]); // vertex rlDeleteBuffers(model.mesh.vboId[1]); // texcoords rlDeleteBuffers(model.mesh.vboId[2]); // normals - //rlDeleteBuffers(model.mesh.vboId[3]); // colors (NOT USED) - //rlDeleteBuffers(model.mesh.vboId[4]); // tangents (NOT USED) - //rlDeleteBuffers(model.mesh.vboId[5]); // texcoords2 (NOT USED) + rlDeleteBuffers(model.mesh.vboId[3]); // colors + rlDeleteBuffers(model.mesh.vboId[4]); // tangents + rlDeleteBuffers(model.mesh.vboId[5]); // texcoords2 rlDeleteVertexArrays(model.mesh.vaoId); } @@ -672,7 +755,7 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) { #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3) - Mesh mesh; + Mesh mesh = { 0 }; int mapX = heightmap.width; int mapZ = heightmap.height; @@ -687,7 +770,7 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); - mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char)); // Not used... + mesh.colors = NULL; int vCounter = 0; // Used to count vertices float by float int tcCounter = 0; // Used to count texcoords float by float @@ -770,16 +853,12 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) free(pixels); - // Fill color data - // NOTE: Not used any more... just one plain color defined at DrawModel() - for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255; - return mesh; } static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) { - Mesh mesh; + Mesh mesh = { 0 }; Color *cubicmapPixels = GetImageData(cubicmap); @@ -1088,11 +1167,7 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); - mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char)); // Not used... - - // Fill color data - // NOTE: Not used any more... just one plain color defined at DrawModel() - for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255; + mesh.colors = NULL; int fCounter = 0; @@ -1810,7 +1885,7 @@ static Mesh LoadOBJ(const char *fileName) mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char)); + mesh.colors = NULL; int vCounter = 0; // Used to count vertices float by float int tcCounter = 0; // Used to count texcoords float by float @@ -1909,10 +1984,6 @@ static Mesh LoadOBJ(const char *fileName) // Security check, just in case no normals or no texcoords defined in OBJ if (numTexCoords == 0) for (int i = 0; i < (2*mesh.vertexCount); i++) mesh.texcoords[i] = 0.0f; - - // NOTE: We set all vertex colors to white - // NOTE: Not used any more... just one plain color defined at DrawModel() - for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255; // Now we can free temp mid* arrays free(midVertices); @@ -1945,9 +2016,6 @@ static Material LoadMTL(const char *fileName) return material; } - // First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles - // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) - // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) while(!feof(mtlFile)) { fscanf(mtlFile, "%c", &dataType); diff --git a/src/raylib.h b/src/raylib.h index c88a60f45..603844449 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -386,7 +386,7 @@ typedef struct Mesh { typedef struct Shader { unsigned int id; // Shader program id - // Variable attributes locations + // Vertex attributes locations (default locations) int vertexLoc; // Vertex attribute location point (default-location = 0) int texcoordLoc; // Texcoord attribute location point (default-location = 1) int normalLoc; // Normal attribute location point (default-location = 2) @@ -803,14 +803,14 @@ void DrawGizmo(Vector3 position); //------------------------------------------------------------------------------------ Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) Model LoadModelEx(Mesh data); // Load a 3d model (from mesh data) -//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) +Model LoadModelFromRES(const char *rresName, int resId); // Load a 3d model from rRES file (raylib Resource) Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) void UnloadModel(Model model); // Unload 3d model from memory void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model Material LoadMaterial(const char *fileName); // Load material data (from file) -Material LoadDefaultMaterial(void); // Load default material (uses default models shader) +Material LoadDefaultMaterial(void); // Load default material (uses default models shader) void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters diff --git a/src/rlgl.c b/src/rlgl.c index 462ccdec4..6ffcb8a5d 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -811,9 +811,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 (!vaoSupported) TraceLog(INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + if (id != 0) + { + glDeleteBuffers(1, &id); + if (!vaoSupported) TraceLog(INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + } #endif } @@ -1638,6 +1640,7 @@ void rlglGenerateMipmaps(Texture2D texture) } // Upload vertex data into a VAO (if supported) and VBO +// TODO: Consider attributes: color, texcoords2, tangents (if available) void rlglLoadMesh(Mesh *mesh) { mesh->vaoId = 0; // Vertex Array Object @@ -1648,11 +1651,10 @@ void rlglLoadMesh(Mesh *mesh) mesh->vboId[4] = 0; // Vertex tangent VBO mesh->vboId[5] = 0; // Vertex texcoord2 VBO - // TODO: Consider attributes: color, texcoords2, tangents (if available) - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) GLuint vaoId = 0; // Vertex Array Objects (VAO) - GLuint vboId[3]; // Vertex Buffer Objects (VBOs) + GLuint vboId[6]; // Vertex Buffer Objects (VBOs) if (vaoSupported) { @@ -1661,36 +1663,92 @@ void rlglLoadMesh(Mesh *mesh) glBindVertexArray(vaoId); } - // Create buffers for our vertex data (positions, texcoords, normals) - glGenBuffers(3, vboId); - // NOTE: Attributes must be uploaded considering default locations points // 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, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(0); // 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, GL_STATIC_DRAW); glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(1); // Enable vertex attributes: normals (shader-location = 2) - glBindBuffer(GL_ARRAY_BUFFER, vboId[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, GL_STATIC_DRAW); - glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(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, GL_STATIC_DRAW); + 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); + } // Default color vertex attribute (shader-location = 3) - glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); // Color vertex attribute set to default: WHITE - glDisableVertexAttribArray(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, GL_STATIC_DRAW); + 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, GL_STATIC_DRAW); + 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, GL_STATIC_DRAW); + glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(5); + } + else + { + // Default tangents vertex attribute + glVertexAttrib2f(5, 0.0f, 0.0f); + glDisableVertexAttribArray(5); + } 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 if (vaoSupported) { @@ -1703,7 +1761,7 @@ void rlglLoadMesh(Mesh *mesh) } else { - TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vboId[0], mesh->vboId[1], mesh->vboId[2]); + TraceLog(INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); } #endif } From f7d4951165e8bece5ae58cd4c92b08e4f29cbef7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 May 2016 23:50:35 +0200 Subject: [PATCH 07/11] Improved vertex attribs support for models --- src/models.c | 2 +- src/raylib.h | 14 ++++++++------ src/rlgl.c | 45 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/models.c b/src/models.c index 4aff72163..ee04b449f 100644 --- a/src/models.c +++ b/src/models.c @@ -695,7 +695,7 @@ void UnloadModel(Model model) // Unload mesh data free(model.mesh.vertices); free(model.mesh.texcoords); - free(model.mesh.normals); + if (model.mesh.normals != NULL) free(model.mesh.normals); if (model.mesh.colors != NULL) free(model.mesh.colors); if (model.mesh.tangents != NULL) free(model.mesh.tangents); if (model.mesh.texcoords2 != NULL) free(model.mesh.texcoords2); diff --git a/src/raylib.h b/src/raylib.h index 603844449..1258bffc7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -369,12 +369,12 @@ typedef struct BoundingBox { // Vertex data definning a mesh typedef struct Mesh { int vertexCount; // num vertices - float *vertices; // vertex position (XYZ - 3 components per vertex) - float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) - float *texcoords2; // vertex second texture coordinates (useful for lightmaps) - float *normals; // vertex normals (XYZ - 3 components per vertex) - float *tangents; // vertex tangents (XYZ - 3 components per vertex) - unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) + 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) + float *texcoords2; // vertex second texture coordinates (useful for lightmaps) (shader-location = 5) + float *normals; // vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tangents; // vertex tangents (XYZ - 3 components per vertex) (shader-location = 4) + unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) BoundingBox bounds; // mesh limits defined by min and max points @@ -391,6 +391,8 @@ typedef struct Shader { int texcoordLoc; // Texcoord attribute location point (default-location = 1) int normalLoc; // Normal attribute location point (default-location = 2) int colorLoc; // Color attibute location point (default-location = 3) + int tangentLoc; // Tangent attribute location point (default-location = 4) + int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5) // Uniform locations int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) diff --git a/src/rlgl.c b/src/rlgl.c index 6ffcb8a5d..2e4601df7 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1084,12 +1084,13 @@ void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) // 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 + 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 - glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array - //glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array (NOT USED) + 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)); @@ -1099,7 +1100,8 @@ void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array - glDisableClientState(GL_NORMAL_ARRAY); // Disable normals 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); @@ -1170,7 +1172,29 @@ void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) glEnableVertexAttribArray(material.shader.normalLoc); } - // TODO: Bind mesh VBO data: colors, tangents, texcoords2 (if available) + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) , tangents, texcoords2 (if available) + if (material.shader.colorLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(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); + } } // Draw call! @@ -1640,16 +1664,15 @@ void rlglGenerateMipmaps(Texture2D texture) } // Upload vertex data into a VAO (if supported) and VBO -// TODO: Consider attributes: color, texcoords2, tangents (if available) void rlglLoadMesh(Mesh *mesh) { 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 color VBO - mesh->vboId[4] = 0; // Vertex tangent VBO - mesh->vboId[5] = 0; // Vertex texcoord2 VBO + mesh->vboId[3] = 0; // Vertex colors VBO + mesh->vboId[4] = 0; // Vertex tangents VBO + mesh->vboId[5] = 0; // Vertex texcoords2 VBO #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -2314,12 +2337,16 @@ static void LoadDefaultShaderLocations(Shader *shader) // vertex texcoord location = 1 // vertex normal location = 2 // vertex color location = 3 + // vertex tangent location = 4 + // vertex texcoord2 location = 5 // 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"); shader->colorLoc = glGetAttribLocation(shader->id, "vertexColor"); + shader->tangentLoc = glGetAttribLocation(shader->id, "vertexTangent"); + shader->texcoord2Loc = glGetAttribLocation(shader->id, "vertexTexCoord2"); // Get handles to GLSL uniform locations (vertex shader) shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix"); From dc4d5dabcdf8f35f32acb9c5b48a24b1141c460f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 May 2016 01:18:46 +0200 Subject: [PATCH 08/11] Added MTL loading info --- src/models.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/models.c b/src/models.c index ee04b449f..7b6deb5c6 100644 --- a/src/models.c +++ b/src/models.c @@ -2001,7 +2001,26 @@ static Material LoadMTL(const char *fileName) { Material material = { 0 }; - // TODO: Load mtl file + // TODO: Load mtl file (multiple variations of .mtl format) + /* + newmtl string Material newmtl (material name). Begins a new material description. + Ka float float float Ambient color Ka (red) (green) (blue) + Kd float float float Diffuse color Kd (red) (green) (blue) + Ks float float float Specular color Ks (red) (green) (blue) + Ke float float float Emmisive color + d float Tr float Dissolve factor. Transparency Tr (alpha). d is inverse of Tr + Ns int Shininess Ns (specular power). Ranges from 0 to 1000. Specular exponent. + Ni int Refraction index. + illum int Illumination model illum (1 / 2); 1 if specular disabled, 2 if specular enabled (lambertian model) + map_Kd string Texture map_Kd (filename) + map_Kd string Diffuse color texture map. + map_Ks string Specular color texture map. + map_Ka string Ambient color texture map. + map_Bump string Bump texture map. Alternative: bump string / map_bump string + map_d string Opacity texture map. + disp string Displacement map + refl Reflection type and map + */ char dataType; char comments[200]; From 3d0208223ad5b79cb4c3d6cd367d39f9d4e51662 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 May 2016 12:40:59 +0200 Subject: [PATCH 09/11] First implementation of MTL loading Not tested yet --- src/models.c | 156 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 132 insertions(+), 24 deletions(-) diff --git a/src/models.c b/src/models.c index 7b6deb5c6..dff6b224f 100644 --- a/src/models.c +++ b/src/models.c @@ -1997,33 +1997,16 @@ static Mesh LoadOBJ(const char *fileName) } // Load MTL material data +// NOTE: Texture map parameters are not supported static Material LoadMTL(const char *fileName) { - Material material = { 0 }; + #define MAX_BUFFER_SIZE 128 - // TODO: Load mtl file (multiple variations of .mtl format) - /* - newmtl string Material newmtl (material name). Begins a new material description. - Ka float float float Ambient color Ka (red) (green) (blue) - Kd float float float Diffuse color Kd (red) (green) (blue) - Ks float float float Specular color Ks (red) (green) (blue) - Ke float float float Emmisive color - d float Tr float Dissolve factor. Transparency Tr (alpha). d is inverse of Tr - Ns int Shininess Ns (specular power). Ranges from 0 to 1000. Specular exponent. - Ni int Refraction index. - illum int Illumination model illum (1 / 2); 1 if specular disabled, 2 if specular enabled (lambertian model) - map_Kd string Texture map_Kd (filename) - map_Kd string Diffuse color texture map. - map_Ks string Specular color texture map. - map_Ka string Ambient color texture map. - map_Bump string Bump texture map. Alternative: bump string / map_bump string - map_d string Opacity texture map. - disp string Displacement map - refl Reflection type and map - */ + Material material = { 0 }; // LoadDefaultMaterial(); - char dataType; - char comments[200]; + char buffer[MAX_BUFFER_SIZE]; + Vector3 color = { 1.0f, 1.0f, 1.0f }; + char *mapFileName; FILE *mtlFile; @@ -2037,7 +2020,132 @@ static Material LoadMTL(const char *fileName) while(!feof(mtlFile)) { - fscanf(mtlFile, "%c", &dataType); + fgets(buffer, MAX_BUFFER_SIZE, mtlFile); + + switch (buffer[0]) + { + case 'n': // newmtl string Material name. Begins a new material description. + { + // TODO: Support multiple materials in a single .mtl + sscanf(buffer, "newmtl %s", mapFileName); + + TraceLog(INFO, "[%s] Loading material...", mapFileName); + } + case 'i': // illum int Illumination model + { + // illum = 1 if specular disabled + // illum = 2 if specular enabled (lambertian model) + // ... + } + case 'K': // Ka, Kd, Ks, Ke + { + switch (buffer[1]) + { + case 'a': // Ka float float float Ambient color (RGB) + { + sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z); + material.colAmbient.r = (unsigned char)(color.x*255); + material.colAmbient.g = (unsigned char)(color.y*255); + material.colAmbient.b = (unsigned char)(color.z*255); + } break; + case 'd': // Kd float float float Diffuse color (RGB) + { + sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z); + material.colDiffuse.r = (unsigned char)(color.x*255); + material.colDiffuse.g = (unsigned char)(color.y*255); + material.colDiffuse.b = (unsigned char)(color.z*255); + } break; + case 's': // Ks float float float Specular color (RGB) + { + sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z); + material.colSpecular.r = (unsigned char)(color.x*255); + material.colSpecular.g = (unsigned char)(color.y*255); + material.colSpecular.b = (unsigned char)(color.z*255); + } break; + case 'e': // Ke float float float Emmisive color (RGB) + { + // TODO: Support Ke ? + } break; + default: break; + } + } break; + case 'N': // Ns, Ni + { + if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000. + { + sscanf(buffer, "Ns %i", &material.glossiness); + } + else if (buffer[1] == 'i') // Ni int Refraction index. + { + // Not supported... + } + } break; + case 'm': // map_Kd, map_Ks, map_Ka, map_Bump, map_d + { + switch (buffer[4]) + { + case 'K': // Color texture maps + { + if (buffer[5] == 'd') // map_Kd string Diffuse color texture map. + { + sscanf(buffer, "map_Kd %s", mapFileName); + if (mapFileName != NULL) material.texDiffuse = LoadTexture(mapFileName); + } + else if (buffer[5] == 's') // map_Ks string Specular color texture map. + { + sscanf(buffer, "map_Ks %s", mapFileName); + if (mapFileName != NULL) material.texSpecular = LoadTexture(mapFileName); + } + else if (buffer[5] == 'a') // map_Ka string Ambient color texture map. + { + // Not supported... + } + } break; + case 'B': // map_Bump string Bump texture map. + { + sscanf(buffer, "map_Bump %s", mapFileName); + if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName); + } break; + case 'b': // map_bump string Bump texture map. + { + sscanf(buffer, "map_bump %s", mapFileName); + if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName); + } break; + case 'd': // map_d string Opacity texture map. + { + // Not supported... + } break; + default: break; + } + } break; + case 'd': // d, disp + { + if (buffer[1] == ' ') // d float Dissolve factor. d is inverse of Tr + { + float alpha = 1.0f; + sscanf(buffer, "d %f", &alpha); + material.colDiffuse.a = (unsigned char)(alpha*255); + } + else if (buffer[1] == 'i') // disp string Displacement map + { + // Not supported... + } + } break; + case 'b': // bump string Bump texture map + { + sscanf(buffer, "bump %s", mapFileName); + if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName); + } break; + case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d + { + float ialpha = 0.0f; + sscanf(buffer, "Tr %f", &ialpha); + material.colDiffuse.a = (unsigned char)((1.0f - ialpha)*255); + + } break; + case 'r': // refl string Reflection texture map + default: break; + } } fclose(mtlFile); From c85cd290491baa7be2b107bdb72c4b039dfd8cb3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 May 2016 12:41:53 +0200 Subject: [PATCH 10/11] Added defines for default shader names --- src/rlgl.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/rlgl.c b/src/rlgl.c index 2e4601df7..33f12debc 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -115,6 +115,15 @@ #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #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 //---------------------------------------------------------------------------------- @@ -2220,12 +2229,12 @@ static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr) glAttachShader(program, fragmentShader); // NOTE: Default attribute shader locations must be binded before linking - glBindAttribLocation(program, 0, "vertexPosition"); - glBindAttribLocation(program, 1, "vertexTexCoord"); - glBindAttribLocation(program, 2, "vertexNormal"); - glBindAttribLocation(program, 3, "vertexColor"); - glBindAttribLocation(program, 4, "vertexTangent"); - glBindAttribLocation(program, 5, "vertexTexCoord2"); + 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 From ac44db26a2a2bed901c7e75d96e215fa09bd3705 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 May 2016 13:16:44 +0200 Subject: [PATCH 11/11] Added reference --- src/models.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models.c b/src/models.c index dff6b224f..7d24e383e 100644 --- a/src/models.c +++ b/src/models.c @@ -1996,7 +1996,7 @@ static Mesh LoadOBJ(const char *fileName) return mesh; } -// Load MTL material data +// Load MTL material data (specs: http://paulbourke.net/dataformats/mtl/) // NOTE: Texture map parameters are not supported static Material LoadMTL(const char *fileName) {