nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : CAPI_GL_HSWDisplay.cpp nuclear@0: Content : Implements Health and Safety Warning system. nuclear@0: Created : July 7, 2014 nuclear@0: Authors : Paul Pedriana nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: nuclear@0: #include "CAPI_GL_HSWDisplay.h" nuclear@0: #include "CAPI_GL_DistortionShaders.h" nuclear@0: #include "../../OVR_CAPI_GL.h" nuclear@0: #include "../../Kernel/OVR_File.h" nuclear@0: #include "../../Kernel/OVR_Math.h" nuclear@0: #include "../../Kernel/OVR_Allocator.h" nuclear@0: #include "../../Kernel/OVR_Color.h" nuclear@0: nuclear@0: nuclear@0: OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..." nuclear@0: nuclear@0: nuclear@0: namespace OVR { namespace CAPI { nuclear@0: nuclear@0: nuclear@0: // Loads the TGA data from the File as an array of width * height 32 bit Texture_RGBA values. nuclear@0: // Returned pointer must be freed with OVR_FREE. nuclear@0: uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height) nuclear@0: { nuclear@0: // See http://www.fileformat.info/format/tga/egff.htm for format details. nuclear@0: // TGA files are stored with little-endian data. nuclear@0: uint8_t* pRGBA = NULL; nuclear@0: nuclear@0: f->SeekToBegin(); nuclear@0: nuclear@0: const int desclen = f->ReadUByte(); nuclear@0: const int palette = f->ReadUByte(); nuclear@0: OVR_UNUSED(palette); nuclear@0: const int imgtype = f->ReadUByte(); nuclear@0: f->ReadUInt16(); // Skip bytes nuclear@0: int palCount = f->ReadUInt16(); nuclear@0: int palSize = f->ReadUByte(); nuclear@0: f->ReadUInt16(); nuclear@0: f->ReadUInt16(); nuclear@0: width = f->ReadUInt16(); nuclear@0: height = f->ReadUInt16(); nuclear@0: int bpp = f->ReadUByte(); nuclear@0: f->ReadUByte(); nuclear@0: nuclear@0: const int ImgTypeBGRAUncompressed = 2; nuclear@0: const int ImgTypeBGRARLECompressed = 10; nuclear@0: nuclear@0: OVR_ASSERT(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))); nuclear@0: nuclear@0: // imgType 2 is uncompressed true-color image. nuclear@0: // imgType 10 is run-length encoded true-color image. nuclear@0: if(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))) nuclear@0: { nuclear@0: int imgsize = width * height * 4; nuclear@0: pRGBA = (uint8_t*) OVR_ALLOC(imgsize); nuclear@0: f->Skip(desclen); nuclear@0: f->Skip(palCount * (palSize + 7) >> 3); nuclear@0: int strideBytes = width * 4; // This is the number of bytes between successive rows. nuclear@0: nuclear@0: unsigned char buf[4] = { 0, 0, 0, alpha }; // If bpp is 24 then this alpha will be unmodified below. nuclear@0: nuclear@0: switch (imgtype) nuclear@0: { nuclear@0: case ImgTypeBGRAUncompressed: nuclear@0: switch (bpp) nuclear@0: { nuclear@0: case 24: nuclear@0: case 32: nuclear@0: for (int y = 0; y < height; y++) nuclear@0: { nuclear@0: for (int x = 0; x < width; x++) nuclear@0: { nuclear@0: f->Read(buf, bpp / 8); // Data is stored as B, G, R nuclear@0: pRGBA[y*strideBytes + x*4 + 0] = buf[2]; nuclear@0: pRGBA[y*strideBytes + x*4 + 1] = buf[1]; nuclear@0: pRGBA[y*strideBytes + x*4 + 2] = buf[0]; nuclear@0: pRGBA[y*strideBytes + x*4 + 3] = buf[3]; nuclear@0: } nuclear@0: } nuclear@0: break; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case ImgTypeBGRARLECompressed: nuclear@0: switch (bpp) nuclear@0: { nuclear@0: case 24: nuclear@0: case 32: nuclear@0: for (int y = 0; y < height; y++) // RLE spans don't cross successive rows. nuclear@0: { nuclear@0: int x = 0; nuclear@0: nuclear@0: while(x < width) nuclear@0: { nuclear@0: uint8_t rleByte; nuclear@0: f->Read(&rleByte, 1); nuclear@0: nuclear@0: if(rleByte & 0x80) // If the high byte is set then what follows are RLE bytes. nuclear@0: { nuclear@0: size_t rleCount = ((rleByte & 0x7f) + 1); nuclear@0: f->Read(buf, bpp / 8); // Data is stored as B, G, R, A nuclear@0: nuclear@0: for (; rleCount; --rleCount, ++x) nuclear@0: { nuclear@0: pRGBA[y*strideBytes + x*4 + 0] = buf[2]; nuclear@0: pRGBA[y*strideBytes + x*4 + 1] = buf[1]; nuclear@0: pRGBA[y*strideBytes + x*4 + 2] = buf[0]; nuclear@0: pRGBA[y*strideBytes + x*4 + 3] = buf[3]; nuclear@0: } nuclear@0: } nuclear@0: else // Else what follows are regular bytes of a count indicated by rleByte nuclear@0: { nuclear@0: for (size_t rleCount = (rleByte + 1); rleCount; --rleCount, ++x) nuclear@0: { nuclear@0: f->Read(buf, bpp / 8); // Data is stored as B, G, R, A nuclear@0: pRGBA[y*strideBytes + x*4 + 0] = buf[2]; nuclear@0: pRGBA[y*strideBytes + x*4 + 1] = buf[1]; nuclear@0: pRGBA[y*strideBytes + x*4 + 2] = buf[0]; nuclear@0: pRGBA[y*strideBytes + x*4 + 3] = buf[3]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: break; nuclear@0: } nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return pRGBA; nuclear@0: } // LoadTextureTgaData nuclear@0: nuclear@0: nuclear@0: nuclear@0: namespace GL { nuclear@0: nuclear@0: nuclear@0: // To do: This needs to be promoted to a central version, possibly in CAPI_HSWDisplay.h nuclear@0: struct HASWVertex nuclear@0: { nuclear@0: Vector3f Pos; nuclear@0: Color C; nuclear@0: float U, V; nuclear@0: nuclear@0: HASWVertex(const Vector3f& p, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) nuclear@0: : Pos(p), C(c), U(u), V(v) nuclear@0: {} nuclear@0: nuclear@0: HASWVertex(float x, float y, float z, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) nuclear@0: : Pos(x,y,z), C(c), U(u), V(v) nuclear@0: {} nuclear@0: nuclear@0: bool operator==(const HASWVertex& b) const nuclear@0: { nuclear@0: return (Pos == b.Pos) && (C == b.C) && (U == b.U) && (V == b.V); nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: nuclear@0: // This is a temporary function implementation, and it functionality needs to be implemented in a more generic way. nuclear@0: Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, uint8_t alpha) nuclear@0: { nuclear@0: OVR::CAPI::GL::Texture* pTexture = NULL; nuclear@0: nuclear@0: int width, height; nuclear@0: const uint8_t* pRGBA = LoadTextureTgaData(f, alpha, width, height); nuclear@0: nuclear@0: if (pRGBA) nuclear@0: { nuclear@0: pTexture = new OVR::CAPI::GL::Texture(&rParams, width, height); nuclear@0: nuclear@0: // SetSampleMode forces the use of mipmaps through GL_LINEAR_MIPMAP_LINEAR. nuclear@0: pTexture->SetSampleMode(samplerMode); // Calls glBindTexture internally. nuclear@0: nuclear@0: // We are intentionally not using mipmaps. We need to use this because Texture::SetSampleMode unilaterally uses GL_LINEAR_MIPMAP_LINEAR. nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); nuclear@0: OVR_ASSERT(glGetError() == 0); nuclear@0: nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); nuclear@0: OVR_ASSERT(glGetError() == 0); nuclear@0: nuclear@0: // With OpenGL 4.2+ we can use this instead of glTexImage2D: nuclear@0: // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); nuclear@0: // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); nuclear@0: nuclear@0: OVR_FREE(const_cast(pRGBA)); nuclear@0: } nuclear@0: nuclear@0: return pTexture; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Loads a texture from a memory image of a TGA file. nuclear@0: Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* pData, int dataSize, uint8_t alpha) nuclear@0: { nuclear@0: MemoryFile memoryFile("", pData, dataSize); nuclear@0: nuclear@0: return LoadTextureTga(rParams, samplerMode, &memoryFile, alpha); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: nuclear@0: // The texture below may conceivably be shared between HSWDisplay instances. However, nuclear@0: // beware that sharing may not be possible if two HMDs are using different locales nuclear@0: // simultaneously. As of this writing it's not clear if that can occur in practice. nuclear@0: nuclear@0: HSWDisplay::HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState) nuclear@0: : OVR::CAPI::HSWDisplay(api, hmd, renderState) nuclear@0: , RenderParams() nuclear@0: , GLContext() nuclear@0: , FrameBuffer(0) nuclear@0: , pTexture() nuclear@0: , pShaderSet() nuclear@0: , pVertexShader() nuclear@0: , pFragmentShader() nuclear@0: , pVB() nuclear@0: , VAO(0) nuclear@0: , VAOInitialized(false) nuclear@0: , OrthoProjection() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: nuclear@0: bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig) nuclear@0: { nuclear@0: const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; nuclear@0: nuclear@0: if(config) nuclear@0: { nuclear@0: // The following is essentially copied from CAPI_GL_DistortionRender.cpp's nuclear@0: // Initialize function. To do: Merge this to a central location. nuclear@0: RenderParams.Multisample = config->OGL.Header.Multisample; nuclear@0: RenderParams.BackBufferSize = config->OGL.Header.BackBufferSize; nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: RenderParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); nuclear@0: RenderParams.DC = config->OGL.DC; nuclear@0: #elif defined(OVR_OS_LINUX) nuclear@0: if (config->OGL.Disp) nuclear@0: { nuclear@0: RenderParams.Disp = config->OGL.Disp; nuclear@0: } nuclear@0: if (!RenderParams.Disp) nuclear@0: { nuclear@0: RenderParams.Disp = glXGetCurrentDisplay(); nuclear@0: } nuclear@0: if (!RenderParams.Disp) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("glXGetCurrentDisplay failed.")); nuclear@0: return false; nuclear@0: } nuclear@0: #endif nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: UnloadGraphics(); nuclear@0: } nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::Shutdown() nuclear@0: { nuclear@0: UnloadGraphics(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::DisplayInternal() nuclear@0: { nuclear@0: HSWDISPLAY_LOG(("[HSWDisplay GL] DisplayInternal()")); nuclear@0: // We may want to call LoadGraphics here instead of within Render. nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::DismissInternal() nuclear@0: { nuclear@0: HSWDISPLAY_LOG(("[HSWDisplay GL] DismissInternal()")); nuclear@0: UnloadGraphicsRequested = true; // We don't directly call UnloadGraphics here because this may be executed within a different thread. nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::UnloadGraphics() nuclear@0: { nuclear@0: if(pTexture) // If initialized... nuclear@0: { nuclear@0: Context currentGLContext; nuclear@0: currentGLContext.InitFromCurrent(); nuclear@0: GLContext.Bind(); nuclear@0: nuclear@0: // RenderParams: No need to clear. nuclear@0: if(FrameBuffer != 0) nuclear@0: { nuclear@0: glDeleteFramebuffers(1, &FrameBuffer); nuclear@0: FrameBuffer = 0; nuclear@0: } nuclear@0: pTexture.Clear(); nuclear@0: pShaderSet.Clear(); nuclear@0: pVertexShader.Clear(); nuclear@0: pFragmentShader.Clear(); nuclear@0: pVB.Clear(); nuclear@0: if(VAO) nuclear@0: { nuclear@0: glDeleteVertexArrays(1, &VAO); nuclear@0: currentGLContext.Bind(); nuclear@0: GLContext.Destroy(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::LoadGraphics() nuclear@0: { nuclear@0: // We assume here that the current GL context is the one our resources will be associated with. nuclear@0: nuclear@0: if (FrameBuffer == 0) nuclear@0: { nuclear@0: glGenFramebuffers(1, &FrameBuffer); nuclear@0: } nuclear@0: nuclear@0: if (!pTexture) // To do: Add support for .dds files, which would be significantly smaller than the size of the tga. nuclear@0: { nuclear@0: size_t textureSize; nuclear@0: const uint8_t* TextureData = GetDefaultTexture(textureSize); nuclear@0: pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, TextureData, (int)textureSize, 255); nuclear@0: } nuclear@0: nuclear@0: if (!pShaderSet) nuclear@0: { nuclear@0: pShaderSet = *new ShaderSet(); nuclear@0: } nuclear@0: nuclear@0: if(!pVertexShader) nuclear@0: { nuclear@0: OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix); nuclear@0: strShader += SimpleTexturedQuad_vs; nuclear@0: nuclear@0: pVertexShader = *new VertexShader(&RenderParams, const_cast(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_vs_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_vs_refl)); nuclear@0: pShaderSet->SetShader(pVertexShader); nuclear@0: } nuclear@0: nuclear@0: if(!pFragmentShader) nuclear@0: { nuclear@0: OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix); nuclear@0: strShader += SimpleTexturedQuad_ps; nuclear@0: nuclear@0: pFragmentShader = *new FragmentShader(&RenderParams, const_cast(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_ps_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_ps_refl)); nuclear@0: pShaderSet->SetShader(pFragmentShader); nuclear@0: } nuclear@0: nuclear@0: if(!pVB) nuclear@0: { nuclear@0: pVB = *new Buffer(&RenderParams); nuclear@0: nuclear@0: pVB->Data(Buffer_Vertex, NULL, 4 * sizeof(HASWVertex)); nuclear@0: HASWVertex* pVertices = (HASWVertex*)pVB->Map(0, 4 * sizeof(HASWVertex), Map_Discard); nuclear@0: OVR_ASSERT(pVertices); nuclear@0: nuclear@0: if(pVertices) nuclear@0: { nuclear@0: const bool flip = ((RenderState.DistortionCaps & ovrDistortionCap_FlipInput) != 0); nuclear@0: const float left = -1.0f; // We currently draw this in normalized device coordinates with an stereo translation nuclear@0: const float top = -1.1f; // applied as a vertex shader uniform. In the future when we have a more formal graphics nuclear@0: const float right = 1.0f; // API abstraction we may move this draw to an overlay layer or to a more formal nuclear@0: const float bottom = 0.9f; // model/mesh scheme with a perspective projection. nuclear@0: nuclear@0: pVertices[0] = HASWVertex(left, top, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f); nuclear@0: pVertices[1] = HASWVertex(left, bottom, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 0.f : 1.f); nuclear@0: pVertices[2] = HASWVertex(right, top, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 1.f : 0.f); nuclear@0: pVertices[3] = HASWVertex(right, bottom, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 0.f : 1.f); nuclear@0: nuclear@0: pVB->Unmap(pVertices); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // We don't bind or initialize the vertex arrays here. nuclear@0: if (!VAO && GLE_ARB_vertex_array_object) nuclear@0: { nuclear@0: OVR_ASSERT(!VAOInitialized); nuclear@0: glGenVertexArrays(1, &VAO); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) nuclear@0: { nuclear@0: if(RenderEnabled && eyeTexture) nuclear@0: { nuclear@0: // We need to render to the eyeTexture with the texture viewport. nuclear@0: // Setup rendering to the texture. nuclear@0: ovrGLTexture* eyeTextureGL = const_cast(reinterpret_cast(eyeTexture)); nuclear@0: OVR_ASSERT(eyeTextureGL->Texture.Header.API == ovrRenderAPI_OpenGL); nuclear@0: nuclear@0: GL::AutoContext autoGLContext(GLContext); // Saves the current GL context, binds our GLContext, then at the end of scope re-binds the current GL context. nuclear@0: nuclear@0: // Load the graphics if not loaded already. nuclear@0: if (!pTexture) nuclear@0: LoadGraphics(); nuclear@0: nuclear@0: // Calculate ortho projection. nuclear@0: GetOrthoProjection(RenderState, OrthoProjection); nuclear@0: nuclear@0: // Set the rendering to be to the eye texture. nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, FrameBuffer); nuclear@0: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eyeTextureGL->OGL.TexId, 0); nuclear@0: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); // We aren't using depth, as we currently want this to overwrite everything. nuclear@0: GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); nuclear@0: OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status); nuclear@0: nuclear@0: // Set up the viewport nuclear@0: const GLint x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x; nuclear@0: const GLint y = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.y; // Note that GL uses bottom-up coordinates. nuclear@0: const GLsizei w = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.w; nuclear@0: const GLsizei h = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.h; nuclear@0: glViewport(x, y, w, h); nuclear@0: nuclear@0: // Set fixed-function render states. nuclear@0: //glDepthRange(0.0, 1.0); // This is the default nuclear@0: glDepthMask(GL_FALSE); nuclear@0: glDisable(GL_DEPTH_TEST); nuclear@0: glFrontFace(GL_CW); nuclear@0: glEnable(GL_BLEND); nuclear@0: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); nuclear@0: nuclear@0: // Enable the buffer and shaders we use. nuclear@0: ShaderFill fill(pShaderSet); nuclear@0: if (pTexture) nuclear@0: fill.SetTexture(0, pTexture); nuclear@0: nuclear@0: // Set shader uniforms. nuclear@0: const float scale = HSWDISPLAY_SCALE * ((RenderState.OurHMDInfo.HmdType == HmdType_DK1) ? 0.70f : 1.f); nuclear@0: pShaderSet->SetUniform2f("Scale", scale, scale / 2.f); // X and Y scale. Y is a fixed proportion to X in order to give a certain aspect ratio. nuclear@0: pShaderSet->SetUniform2f("PositionOffset", OrthoProjection[eye].GetTranslation().x, 0.0f); nuclear@0: nuclear@0: // Set vertex attributes nuclear@0: if (GLE_ARB_vertex_array_object) nuclear@0: { nuclear@0: OVR_ASSERT(VAO != 0); nuclear@0: glBindVertexArray(VAO); nuclear@0: } nuclear@0: nuclear@0: if(!VAOInitialized) // This executes for the case that VAO isn't supported. nuclear@0: { nuclear@0: glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); // This must be called before glVertexAttribPointer is called below. nuclear@0: nuclear@0: const GLuint shaderProgram = pShaderSet->Prog; nuclear@0: GLint attributeLocationArray[3]; nuclear@0: nuclear@0: attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position"); nuclear@0: glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast(offsetof(HASWVertex, Pos))); nuclear@0: nuclear@0: attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color"); nuclear@0: glVertexAttribPointer(attributeLocationArray[1], sizeof(Color)/sizeof(uint8_t), GL_UNSIGNED_BYTE, true, sizeof(HASWVertex), reinterpret_cast(offsetof(HASWVertex, C))); // True because we want it to convert [0,255] to [0,1] for us. nuclear@0: nuclear@0: attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord"); nuclear@0: glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast(offsetof(HASWVertex, U))); nuclear@0: nuclear@0: for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) nuclear@0: glEnableVertexAttribArray((GLuint)i); nuclear@0: } nuclear@0: nuclear@0: fill.Set(Prim_TriangleStrip); nuclear@0: nuclear@0: glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); nuclear@0: nuclear@0: if (GLE_ARB_vertex_array_object) nuclear@0: { nuclear@0: VAOInitialized = true; nuclear@0: glBindVertexArray(0); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: }}} // namespace OVR::CAPI::GL