diff --git a/src/rlgl.c b/src/rlgl.c index 7cb6a751e..65249f5ea 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -31,7 +31,7 @@ #include // Required for: fopen(), fclose(), fread()... [Used only on ReadTextFile()] #include // Required for: malloc(), free(), rand() #include // Required for: strcmp(), strlen(), strtok() -#include // Required for: atan() +#include // Required for: atan2() #ifndef RLGL_STANDALONE #include "raymath.h" // Required for Vector3 and Matrix functions @@ -206,6 +206,15 @@ typedef struct { float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; +// VR Stereo rendering configuration for simulator +typedef struct { + RenderTexture2D stereoFbo; // VR stereo rendering framebuffer + Shader distortionShader; // VR stereo rendering distortion shader + //Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices +} VrStereoConfig; + #if defined(RLGL_OCULUS_SUPPORT) typedef struct OculusBuffer { ovrTextureSwapChain textureChain; @@ -292,19 +301,15 @@ static OculusMirror mirror; // Oculus mirror texture and fbo static unsigned int frameIndex = 0; // Oculus frames counter, used to discard frames from chain #endif +// VR global variables +static VrDeviceInfo hmd; // Current VR device info +static VrStereoConfig vrConfig; // VR stereo configuration for simulator static bool vrDeviceReady = false; // VR device ready flag static bool vrSimulator = false; // VR simulator enabled flag static bool vrEnabled = false; // VR experience enabled (device or simulator) static bool vrRendering = true; // VR stereo rendering enabled/disabled flag // NOTE: This flag is useful to render data over stereo image (i.e. FPS) -static RenderTexture2D stereoFbo; // Stereo rendering framebuffer -static Shader distortionShader; // Stereo rendering distortion shader (simulator) - -// Compressed textures support flags -static bool texCompDXTSupported = false; // DDS texture compression support -static bool npotSupported = false; // NPOT textures full support - #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; @@ -313,6 +318,10 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; //static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: Fails in WebGL, omitted #endif +// Compressed textures support flags +static bool texCompDXTSupported = false; // DDS texture compression support +static bool npotSupported = false; // NPOT textures full support + static int blendMode = 0; // Track current blending mode // White texture useful for plain color polys (required by shader) @@ -341,7 +350,7 @@ static void DrawDefaultBuffers(int eyesCount); // Draw default internal buffers static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU // Configure stereo rendering (including distortion shader) with HMD device parameters -static void SetupVrDevice(VrDeviceInfo info); +static void SetStereoConfig(VrDeviceInfo info); // Set internal projection and modelview matrix depending on eyes tracking data static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); @@ -2535,6 +2544,7 @@ void DestroyLight(Light light) // Init VR device (or simulator) // NOTE: If device is not available, it fallbacks to default device (simulator) +// NOTE: It modifies the global variable: VrDeviceInfo hmd void InitVrDevice(int hmdDevice) { switch (hmdDevice) @@ -2560,40 +2570,39 @@ void InitVrDevice(int hmdDevice) if (!vrDeviceReady) { - TraceLog(WARNING, "VR Device not found: Initializing VR Simulator"); + TraceLog(WARNING, "VR Device not found: Initializing VR Simulator (Oculus Rift DK2)"); // Initialize framebuffer and textures for stereo rendering - stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight); + vrConfig.stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight); // Load distortion shader (initialized by default with Oculus Rift CV1 parameters) - distortionShader.id = LoadShaderProgram(vDistortionShaderStr, fDistortionShaderStr); - if (distortionShader.id != 0) LoadDefaultShaderLocations(&distortionShader); - - VrDeviceInfo info = { 0 }; - + vrConfig.distortionShader.id = LoadShaderProgram(vDistortionShaderStr, fDistortionShaderStr); + if (vrConfig.distortionShader.id != 0) LoadDefaultShaderLocations(&vrConfig.distortionShader); + if ((hmdDevice == HMD_DEFAULT_DEVICE) || (hmdDevice == HMD_OCULUS_RIFT_DK2) || (hmdDevice == HMD_OCULUS_RIFT_CV1)) { - info.hResolution = 1280; // HMD horizontal resolution in pixels - info.vResolution = 800; // HMD vertical resolution in pixels - info.hScreenSize = 0.14976f;; // HMD horizontal size in meters - info.vScreenSize = 0.09356f; // HMD vertical size in meters - info.vScreenCenter = 0.04675f; // HMD screen center in meters - info.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - info.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters - info.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters - info.distortionK[0] = 1.0f; // HMD lens distortion constant parameter 0 - info.distortionK[1] = 0.22f; // HMD lens distortion constant parameter 1 - info.distortionK[2] = 0.24f; // HMD lens distortion constant parameter 2 - info.distortionK[3] = 0.0f; // HMD lens distortion constant parameter 3 - info.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - info.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - info.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - info.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + // NOTE: Oculus Rift DK2 parameters + hmd.hResolution = 1280; // HMD horizontal resolution in pixels + hmd.vResolution = 800; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.14976f;; // HMD horizontal size in meters + hmd.vScreenSize = 0.09356f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04675f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters + hmd.distortionK[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.distortionK[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.distortionK[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.distortionK[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 } - SetupVrDevice(info); + SetStereoConfig(hmd); vrSimulator = true; vrEnabled = true; @@ -2608,11 +2617,8 @@ void CloseVrDevice(void) else #endif { - // Unload stereo framebuffer and texture - rlDeleteRenderTextures(stereoFbo); - - // Unload oculus-distortion shader - UnloadShader(distortionShader); + rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture + UnloadShader(vrConfig.distortionShader); // Unload distortion shader } vrDeviceReady = false; @@ -2654,7 +2660,7 @@ void BeginVrDrawing(void) #endif { // Setup framebuffer for stereo rendering - rlEnableRenderTexture(stereoFbo.id); + rlEnableRenderTexture(vrConfig.stereoFbo.id); } // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) @@ -2696,9 +2702,9 @@ void EndVrDrawing(void) rlLoadIdentity(); // Reset internal modelview matrix // Draw RenderTexture (stereoFbo) using distortion shader - currentShader = distortionShader; + currentShader = vrConfig.distortionShader; - rlEnableTexture(stereoFbo.texture.id); + rlEnableTexture(vrConfig.stereoFbo.texture.id); rlPushMatrix(); rlBegin(RL_QUADS); @@ -2711,15 +2717,15 @@ void EndVrDrawing(void) // Bottom-right corner for texture and quad rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(0.0f, stereoFbo.texture.height); + rlVertex2f(0.0f, vrConfig.stereoFbo.texture.height); // Top-right corner for texture and quad rlTexCoord2f(1.0f, 0.0f); - rlVertex2f(stereoFbo.texture.width, stereoFbo.texture.height); + rlVertex2f(vrConfig.stereoFbo.texture.width, vrConfig.stereoFbo.texture.height); // Top-left corner for texture and quad rlTexCoord2f(1.0f, 1.0f); - rlVertex2f(stereoFbo.texture.width, 0.0f); + rlVertex2f(vrConfig.stereoFbo.texture.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -3752,61 +3758,71 @@ static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) #endif // Configure stereo rendering (including distortion shader) with HMD device parameters -static void SetupVrDevice(VrDeviceInfo hmd) +static void SetStereoConfig(VrDeviceInfo hmd) { - // NOTE: fovy value obtained from device parameters (Oculus Rift CV1) - //float fovy = 2.0f*atan((VScreenSize*0.5f)/EyeToScreenDistance)*RAD2DEG; - - //float eyeProjectionShift = ; - //float projectionOffset = (HScreenSize*0.25f - LensSeparationDistance*0.5f)/(float)HScreenSize; - - // Compute aspect ratio and FOV - //float aspect = ((float)hmd.hResolution/2.0f)/(float)hmd.vResolution; + // Compute aspect ratio + //float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; float aspect = (float)screenWidth*0.5f/(float)screenHeight; - - // Fov-y is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance)*RAD2DEG + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25 + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75 - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = fabsf(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.distortionK[0] + + hmd.distortionK[1]*lensRadiusSq + + hmd.distortionK[2]*lensRadiusSq*lensRadiusSq + + hmd.distortionK[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2/normScreenWidth, 2/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5/distortionScale, normScreenHeight*0.5*aspect/distortionScale }; + + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, 2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.distortionK, 4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance)*RAD2DEG // ...but with lens distortion it is increased (see Oculus SDK Documentation) - float radius = -1.0 - (4*(hmd.hScreenSize/4 - hmd.lensSeparationDistance/2)/hmd.hScreenSize); - float distScale = (hmd.distortionK[0] + hmd.distortionK[1]*pow(radius, 2) + hmd.distortionK[2]*pow(radius, 4) + hmd.distortionK[3]*pow(radius, 6)); - //float fovy = 2.0f*atan2(hmd.vScreenSize*distScale, 2*hmd.eyeToScreenDistance)*RAD2DEG; - float fovy = 2.0f*atan((hmd.vScreenSize*0.5f)/hmd.eyeToScreenDistance)*RAD2DEG; + float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance)*RAD2DEG; // Really need distortionScale? // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); - //float projOffset = 4.0f*(hmd.hScreenSize/4 - hmd.interpupillaryDistance/2)/hmd.hScreenSize; - float projOffset = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // NOTE: Projection matrices must be transposed due to raymath convention + MatrixTranspose(&vrConfig.eyesProjection[0]); + MatrixTranspose(&vrConfig.eyesProjection[1]); - //Matrix projLeft = MatrixMultiply(MatrixTranslate(projOffset, 0.0, 0.0), proj); - //matrix projRight = MatrixMultiply(MatrixTranslate(-projOffset, 0.0, 0.0)), proj); - // Compute camera transformation matrices - //Matrix viewTransformLeft = MatrixTranslate(-hmd.interpupillaryDistance/2, 0.0, 0.0 ); - //Matrix viewTransformRight = MatrixTranslate(hmd.interpupillaryDistance/2, 0.0, 0.0 ); - - // Compute eyes Viewports - // Rectangle viewportLeft = { 0, 0, hmd.hResolution/2, hmd.vResolution }; - // Rectangle viewportRight = { hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; - - // Distortion shader parameters - float lensShift = 4*(hmd.hScreenSize/4 - hmd.lensSeparationDistance/2)/hmd.hScreenSize; - float leftLensCenter[2] = { lensShift, 0.0f }; // REVIEW!!! - float rightLensCenter[2] = { -lensShift, 0.0f }; // REVIEW!!! - float leftScreenCenter[2] = { 0.25f, 0.5f }; - float rightScreenCenter[2] = { 0.75f, 0.5f }; + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - float scaleIn[2] = { 1.0f, 1.0f/aspect }; // REVIEW!!! - float scale[2] = { 1.0f/distScale, 1.0f*aspect/distScale }; // REVIEW!!! - - // Distortion shader parameters update - //SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "leftLensCenter"), leftLensCenter, 2); - //SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "rightLensCenter"), rightLensCenter, 2); - SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "leftScreenCenter"), leftScreenCenter, 2); - SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "rightScreenCenter"), rightScreenCenter, 2); + // Compute eyes Viewports + //vrConfig.eyesViewport[0] = (Rectangle){ 0, 0, hmd.hResolution/2, hmd.vResolution }; + //vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; - //SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "scale"), scale, 2); - //SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "scaleIn"), scaleIn, 2); - SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "hmdWarpParam"), hmd.distortionK, 4); - SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4); + //https://forums.oculus.com/vip/discussion/3413/calculating-the-distortion-shader-parameters + //https://vrwiki.wikispaces.com/Theory+%26+practice } // Set internal projection and modelview matrix depending on eyes tracking data @@ -3844,59 +3860,10 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) // Setup viewport and projection/modelview matrices using tracking data rlViewport(eye*screenWidth/2, 0, screenWidth/2, screenHeight); - float HScreenSize = 0.14976f; - float VScreenSize = 0.09356f; // HScreenSize/(1280.0f/800.0f) (DK2) - float VScreenCenter = 0.04675f; // VScreenSize/2 - float EyeToScreenDistance = 0.041f; - float LensSeparationDistance = 0.0635f; // DK2 - float InterpupillaryDistance = 0.064f; // IPD - - // NOTE: fovy value obtained from device parameters (Oculus Rift CV1) - float halfScreenDistance = VScreenSize/2.0f; - float fovy = 2.0f*atan(halfScreenDistance/EyeToScreenDistance)*RAD2DEG; - - float viewCenter = (float)HScreenSize*0.25f; - float eyeProjectionShift = viewCenter - LensSeparationDistance*0.5f; - float projectionCenterOffset = eyeProjectionShift/(float)HScreenSize; //4.0f*eyeProjectionShift/(float)HScreenSize; -/* - static float scale[2] = { 0.25, 0.45 }; - - if (IsKeyDown(KEY_RIGHT)) scale[0] += 0.01; - else if (IsKeyDown(KEY_LEFT)) scale[0] -= 0.01; - else if (IsKeyDown(KEY_UP)) scale[1] += 0.01; - else if (IsKeyDown(KEY_DOWN)) scale[1] -= 0.01; - - SetShaderValue(distortionShader, GetShaderLocation(distortionShader, "Scale"), scale, 2); - - if (IsKeyDown(KEY_N)) IPD += 0.02; - else if (IsKeyDown(KEY_M)) IPD -= 0.02; -*/ - // The matrixes for offsetting the projection and view for each eye, to achieve stereo effect - Vector3 projectionOffset = { -projectionCenterOffset, 0.0f, 0.0f }; - - // Camera movement might seem more natural if we model the head. - // Our axis of rotation is the base of our head, so we might want to add - // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. - Vector3 viewOffset = { -InterpupillaryDistance/2.0f, 0.075f, 0.045f }; - - // Negate the left eye versions - if (eye == 0) - { - projectionOffset.x *= -1.0f; - viewOffset.x *= -1.0f; - } - - // Adjust the view and projection matrixes - // View matrix is translated based on the eye offset - Matrix projCenter = MatrixPerspective(fovy, (double)((float)screenWidth*0.5f)/(double)screenHeight, 0.01, 1000.0); - - Matrix projTranslation = MatrixTranslate(projectionOffset.x, projectionOffset.y, projectionOffset.z); - Matrix viewTranslation = MatrixTranslate(viewOffset.x, viewOffset.y, viewOffset.z); - - eyeProjection = MatrixMultiply(projCenter, projTranslation); // projection - eyeModelView = MatrixMultiply(matModelView, viewTranslation); // modelview + // Apply view offset to modelview matrix + eyeModelView = MatrixMultiply(matModelView, vrConfig.eyesViewOffset[eye]); - MatrixTranspose(&eyeProjection); + eyeProjection = vrConfig.eyesProjection[eye]; } SetMatrixModelview(eyeModelView);