ovr_sdk
diff LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp @ 0:1b39a1b46319
initial 0.4.4
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 14 Jan 2015 06:51:16 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,500 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : CAPI_GL_HSWDisplay.cpp 1.7 +Content : Implements Health and Safety Warning system. 1.8 +Created : July 7, 2014 1.9 +Authors : Paul Pedriana 1.10 + 1.11 +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. 1.12 + 1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 1.15 +which is provided at the time of installation or download, or which 1.16 +otherwise accompanies this software in either electronic or hard copy form. 1.17 + 1.18 +You may obtain a copy of the License at 1.19 + 1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 1.21 + 1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 1.23 +distributed under the License is distributed on an "AS IS" BASIS, 1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.25 +See the License for the specific language governing permissions and 1.26 +limitations under the License. 1.27 + 1.28 +************************************************************************************/ 1.29 + 1.30 + 1.31 +#include "CAPI_GL_HSWDisplay.h" 1.32 +#include "CAPI_GL_DistortionShaders.h" 1.33 +#include "../../OVR_CAPI_GL.h" 1.34 +#include "../../Kernel/OVR_File.h" 1.35 +#include "../../Kernel/OVR_Math.h" 1.36 +#include "../../Kernel/OVR_Allocator.h" 1.37 +#include "../../Kernel/OVR_Color.h" 1.38 + 1.39 + 1.40 +OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..." 1.41 + 1.42 + 1.43 +namespace OVR { namespace CAPI { 1.44 + 1.45 + 1.46 +// Loads the TGA data from the File as an array of width * height 32 bit Texture_RGBA values. 1.47 +// Returned pointer must be freed with OVR_FREE. 1.48 +uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height) 1.49 +{ 1.50 + // See http://www.fileformat.info/format/tga/egff.htm for format details. 1.51 + // TGA files are stored with little-endian data. 1.52 + uint8_t* pRGBA = NULL; 1.53 + 1.54 + f->SeekToBegin(); 1.55 + 1.56 + const int desclen = f->ReadUByte(); 1.57 + const int palette = f->ReadUByte(); 1.58 + OVR_UNUSED(palette); 1.59 + const int imgtype = f->ReadUByte(); 1.60 + f->ReadUInt16(); // Skip bytes 1.61 + int palCount = f->ReadUInt16(); 1.62 + int palSize = f->ReadUByte(); 1.63 + f->ReadUInt16(); 1.64 + f->ReadUInt16(); 1.65 + width = f->ReadUInt16(); 1.66 + height = f->ReadUInt16(); 1.67 + int bpp = f->ReadUByte(); 1.68 + f->ReadUByte(); 1.69 + 1.70 + const int ImgTypeBGRAUncompressed = 2; 1.71 + const int ImgTypeBGRARLECompressed = 10; 1.72 + 1.73 + OVR_ASSERT(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))); 1.74 + 1.75 + // imgType 2 is uncompressed true-color image. 1.76 + // imgType 10 is run-length encoded true-color image. 1.77 + if(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))) 1.78 + { 1.79 + int imgsize = width * height * 4; 1.80 + pRGBA = (uint8_t*) OVR_ALLOC(imgsize); 1.81 + f->Skip(desclen); 1.82 + f->Skip(palCount * (palSize + 7) >> 3); 1.83 + int strideBytes = width * 4; // This is the number of bytes between successive rows. 1.84 + 1.85 + unsigned char buf[4] = { 0, 0, 0, alpha }; // If bpp is 24 then this alpha will be unmodified below. 1.86 + 1.87 + switch (imgtype) 1.88 + { 1.89 + case ImgTypeBGRAUncompressed: 1.90 + switch (bpp) 1.91 + { 1.92 + case 24: 1.93 + case 32: 1.94 + for (int y = 0; y < height; y++) 1.95 + { 1.96 + for (int x = 0; x < width; x++) 1.97 + { 1.98 + f->Read(buf, bpp / 8); // Data is stored as B, G, R 1.99 + pRGBA[y*strideBytes + x*4 + 0] = buf[2]; 1.100 + pRGBA[y*strideBytes + x*4 + 1] = buf[1]; 1.101 + pRGBA[y*strideBytes + x*4 + 2] = buf[0]; 1.102 + pRGBA[y*strideBytes + x*4 + 3] = buf[3]; 1.103 + } 1.104 + } 1.105 + break; 1.106 + } 1.107 + break; 1.108 + 1.109 + case ImgTypeBGRARLECompressed: 1.110 + switch (bpp) 1.111 + { 1.112 + case 24: 1.113 + case 32: 1.114 + for (int y = 0; y < height; y++) // RLE spans don't cross successive rows. 1.115 + { 1.116 + int x = 0; 1.117 + 1.118 + while(x < width) 1.119 + { 1.120 + uint8_t rleByte; 1.121 + f->Read(&rleByte, 1); 1.122 + 1.123 + if(rleByte & 0x80) // If the high byte is set then what follows are RLE bytes. 1.124 + { 1.125 + size_t rleCount = ((rleByte & 0x7f) + 1); 1.126 + f->Read(buf, bpp / 8); // Data is stored as B, G, R, A 1.127 + 1.128 + for (; rleCount; --rleCount, ++x) 1.129 + { 1.130 + pRGBA[y*strideBytes + x*4 + 0] = buf[2]; 1.131 + pRGBA[y*strideBytes + x*4 + 1] = buf[1]; 1.132 + pRGBA[y*strideBytes + x*4 + 2] = buf[0]; 1.133 + pRGBA[y*strideBytes + x*4 + 3] = buf[3]; 1.134 + } 1.135 + } 1.136 + else // Else what follows are regular bytes of a count indicated by rleByte 1.137 + { 1.138 + for (size_t rleCount = (rleByte + 1); rleCount; --rleCount, ++x) 1.139 + { 1.140 + f->Read(buf, bpp / 8); // Data is stored as B, G, R, A 1.141 + pRGBA[y*strideBytes + x*4 + 0] = buf[2]; 1.142 + pRGBA[y*strideBytes + x*4 + 1] = buf[1]; 1.143 + pRGBA[y*strideBytes + x*4 + 2] = buf[0]; 1.144 + pRGBA[y*strideBytes + x*4 + 3] = buf[3]; 1.145 + } 1.146 + } 1.147 + } 1.148 + } 1.149 + break; 1.150 + } 1.151 + break; 1.152 + } 1.153 + } 1.154 + 1.155 + return pRGBA; 1.156 +} // LoadTextureTgaData 1.157 + 1.158 + 1.159 + 1.160 +namespace GL { 1.161 + 1.162 + 1.163 +// To do: This needs to be promoted to a central version, possibly in CAPI_HSWDisplay.h 1.164 +struct HASWVertex 1.165 +{ 1.166 + Vector3f Pos; 1.167 + Color C; 1.168 + float U, V; 1.169 + 1.170 + HASWVertex(const Vector3f& p, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) 1.171 + : Pos(p), C(c), U(u), V(v) 1.172 + {} 1.173 + 1.174 + HASWVertex(float x, float y, float z, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) 1.175 + : Pos(x,y,z), C(c), U(u), V(v) 1.176 + {} 1.177 + 1.178 + bool operator==(const HASWVertex& b) const 1.179 + { 1.180 + return (Pos == b.Pos) && (C == b.C) && (U == b.U) && (V == b.V); 1.181 + } 1.182 +}; 1.183 + 1.184 + 1.185 + 1.186 +// This is a temporary function implementation, and it functionality needs to be implemented in a more generic way. 1.187 +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, uint8_t alpha) 1.188 +{ 1.189 + OVR::CAPI::GL::Texture* pTexture = NULL; 1.190 + 1.191 + int width, height; 1.192 + const uint8_t* pRGBA = LoadTextureTgaData(f, alpha, width, height); 1.193 + 1.194 + if (pRGBA) 1.195 + { 1.196 + pTexture = new OVR::CAPI::GL::Texture(&rParams, width, height); 1.197 + 1.198 + // SetSampleMode forces the use of mipmaps through GL_LINEAR_MIPMAP_LINEAR. 1.199 + pTexture->SetSampleMode(samplerMode); // Calls glBindTexture internally. 1.200 + 1.201 + // We are intentionally not using mipmaps. We need to use this because Texture::SetSampleMode unilaterally uses GL_LINEAR_MIPMAP_LINEAR. 1.202 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 1.203 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); 1.204 + OVR_ASSERT(glGetError() == 0); 1.205 + 1.206 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); 1.207 + OVR_ASSERT(glGetError() == 0); 1.208 + 1.209 + // With OpenGL 4.2+ we can use this instead of glTexImage2D: 1.210 + // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); 1.211 + // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); 1.212 + 1.213 + OVR_FREE(const_cast<uint8_t*>(pRGBA)); 1.214 + } 1.215 + 1.216 + return pTexture; 1.217 +} 1.218 + 1.219 + 1.220 +// Loads a texture from a memory image of a TGA file. 1.221 +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* pData, int dataSize, uint8_t alpha) 1.222 +{ 1.223 + MemoryFile memoryFile("", pData, dataSize); 1.224 + 1.225 + return LoadTextureTga(rParams, samplerMode, &memoryFile, alpha); 1.226 +} 1.227 + 1.228 + 1.229 + 1.230 + 1.231 +// The texture below may conceivably be shared between HSWDisplay instances. However, 1.232 +// beware that sharing may not be possible if two HMDs are using different locales 1.233 +// simultaneously. As of this writing it's not clear if that can occur in practice. 1.234 + 1.235 +HSWDisplay::HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState) 1.236 + : OVR::CAPI::HSWDisplay(api, hmd, renderState) 1.237 + , RenderParams() 1.238 + , GLContext() 1.239 + , FrameBuffer(0) 1.240 + , pTexture() 1.241 + , pShaderSet() 1.242 + , pVertexShader() 1.243 + , pFragmentShader() 1.244 + , pVB() 1.245 + , VAO(0) 1.246 + , VAOInitialized(false) 1.247 + , OrthoProjection() 1.248 +{ 1.249 +} 1.250 + 1.251 + 1.252 +bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig) 1.253 +{ 1.254 + const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; 1.255 + 1.256 + if(config) 1.257 + { 1.258 + // The following is essentially copied from CAPI_GL_DistortionRender.cpp's 1.259 + // Initialize function. To do: Merge this to a central location. 1.260 + RenderParams.Multisample = config->OGL.Header.Multisample; 1.261 + RenderParams.BackBufferSize = config->OGL.Header.BackBufferSize; 1.262 + 1.263 + #if defined(OVR_OS_WIN32) 1.264 + RenderParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); 1.265 + RenderParams.DC = config->OGL.DC; 1.266 + #elif defined(OVR_OS_LINUX) 1.267 + if (config->OGL.Disp) 1.268 + { 1.269 + RenderParams.Disp = config->OGL.Disp; 1.270 + } 1.271 + if (!RenderParams.Disp) 1.272 + { 1.273 + RenderParams.Disp = glXGetCurrentDisplay(); 1.274 + } 1.275 + if (!RenderParams.Disp) 1.276 + { 1.277 + OVR_DEBUG_LOG(("glXGetCurrentDisplay failed.")); 1.278 + return false; 1.279 + } 1.280 + #endif 1.281 + } 1.282 + else 1.283 + { 1.284 + UnloadGraphics(); 1.285 + } 1.286 + 1.287 + return true; 1.288 +} 1.289 + 1.290 + 1.291 +void HSWDisplay::Shutdown() 1.292 +{ 1.293 + UnloadGraphics(); 1.294 +} 1.295 + 1.296 + 1.297 +void HSWDisplay::DisplayInternal() 1.298 +{ 1.299 + HSWDISPLAY_LOG(("[HSWDisplay GL] DisplayInternal()")); 1.300 + // We may want to call LoadGraphics here instead of within Render. 1.301 +} 1.302 + 1.303 + 1.304 +void HSWDisplay::DismissInternal() 1.305 +{ 1.306 + HSWDISPLAY_LOG(("[HSWDisplay GL] DismissInternal()")); 1.307 + UnloadGraphicsRequested = true; // We don't directly call UnloadGraphics here because this may be executed within a different thread. 1.308 +} 1.309 + 1.310 + 1.311 +void HSWDisplay::UnloadGraphics() 1.312 +{ 1.313 + if(pTexture) // If initialized... 1.314 + { 1.315 + Context currentGLContext; 1.316 + currentGLContext.InitFromCurrent(); 1.317 + GLContext.Bind(); 1.318 + 1.319 + // RenderParams: No need to clear. 1.320 + if(FrameBuffer != 0) 1.321 + { 1.322 + glDeleteFramebuffers(1, &FrameBuffer); 1.323 + FrameBuffer = 0; 1.324 + } 1.325 + pTexture.Clear(); 1.326 + pShaderSet.Clear(); 1.327 + pVertexShader.Clear(); 1.328 + pFragmentShader.Clear(); 1.329 + pVB.Clear(); 1.330 + if(VAO) 1.331 + { 1.332 + glDeleteVertexArrays(1, &VAO); 1.333 + currentGLContext.Bind(); 1.334 + GLContext.Destroy(); 1.335 + } 1.336 +} 1.337 +} 1.338 + 1.339 + 1.340 +void HSWDisplay::LoadGraphics() 1.341 +{ 1.342 + // We assume here that the current GL context is the one our resources will be associated with. 1.343 + 1.344 + if (FrameBuffer == 0) 1.345 + { 1.346 + glGenFramebuffers(1, &FrameBuffer); 1.347 + } 1.348 + 1.349 + if (!pTexture) // To do: Add support for .dds files, which would be significantly smaller than the size of the tga. 1.350 + { 1.351 + size_t textureSize; 1.352 + const uint8_t* TextureData = GetDefaultTexture(textureSize); 1.353 + pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, TextureData, (int)textureSize, 255); 1.354 + } 1.355 + 1.356 + if (!pShaderSet) 1.357 + { 1.358 + pShaderSet = *new ShaderSet(); 1.359 + } 1.360 + 1.361 + if(!pVertexShader) 1.362 + { 1.363 + OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix); 1.364 + strShader += SimpleTexturedQuad_vs; 1.365 + 1.366 + pVertexShader = *new VertexShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_vs_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_vs_refl)); 1.367 + pShaderSet->SetShader(pVertexShader); 1.368 + } 1.369 + 1.370 + if(!pFragmentShader) 1.371 + { 1.372 + OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix); 1.373 + strShader += SimpleTexturedQuad_ps; 1.374 + 1.375 + pFragmentShader = *new FragmentShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_ps_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_ps_refl)); 1.376 + pShaderSet->SetShader(pFragmentShader); 1.377 + } 1.378 + 1.379 + if(!pVB) 1.380 + { 1.381 + pVB = *new Buffer(&RenderParams); 1.382 + 1.383 + pVB->Data(Buffer_Vertex, NULL, 4 * sizeof(HASWVertex)); 1.384 + HASWVertex* pVertices = (HASWVertex*)pVB->Map(0, 4 * sizeof(HASWVertex), Map_Discard); 1.385 + OVR_ASSERT(pVertices); 1.386 + 1.387 + if(pVertices) 1.388 + { 1.389 + const bool flip = ((RenderState.DistortionCaps & ovrDistortionCap_FlipInput) != 0); 1.390 + const float left = -1.0f; // We currently draw this in normalized device coordinates with an stereo translation 1.391 + const float top = -1.1f; // applied as a vertex shader uniform. In the future when we have a more formal graphics 1.392 + const float right = 1.0f; // API abstraction we may move this draw to an overlay layer or to a more formal 1.393 + const float bottom = 0.9f; // model/mesh scheme with a perspective projection. 1.394 + 1.395 + pVertices[0] = HASWVertex(left, top, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f); 1.396 + pVertices[1] = HASWVertex(left, bottom, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 0.f : 1.f); 1.397 + pVertices[2] = HASWVertex(right, top, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 1.f : 0.f); 1.398 + pVertices[3] = HASWVertex(right, bottom, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 0.f : 1.f); 1.399 + 1.400 + pVB->Unmap(pVertices); 1.401 + } 1.402 + } 1.403 + 1.404 + // We don't bind or initialize the vertex arrays here. 1.405 + if (!VAO && GLE_ARB_vertex_array_object) 1.406 + { 1.407 + OVR_ASSERT(!VAOInitialized); 1.408 + glGenVertexArrays(1, &VAO); 1.409 + } 1.410 +} 1.411 + 1.412 + 1.413 +void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) 1.414 +{ 1.415 + if(RenderEnabled && eyeTexture) 1.416 + { 1.417 + // We need to render to the eyeTexture with the texture viewport. 1.418 + // Setup rendering to the texture. 1.419 + ovrGLTexture* eyeTextureGL = const_cast<ovrGLTexture*>(reinterpret_cast<const ovrGLTexture*>(eyeTexture)); 1.420 + OVR_ASSERT(eyeTextureGL->Texture.Header.API == ovrRenderAPI_OpenGL); 1.421 + 1.422 + GL::AutoContext autoGLContext(GLContext); // Saves the current GL context, binds our GLContext, then at the end of scope re-binds the current GL context. 1.423 + 1.424 + // Load the graphics if not loaded already. 1.425 + if (!pTexture) 1.426 + LoadGraphics(); 1.427 + 1.428 + // Calculate ortho projection. 1.429 + GetOrthoProjection(RenderState, OrthoProjection); 1.430 + 1.431 + // Set the rendering to be to the eye texture. 1.432 + glBindFramebuffer(GL_FRAMEBUFFER, FrameBuffer); 1.433 + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eyeTextureGL->OGL.TexId, 0); 1.434 + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); // We aren't using depth, as we currently want this to overwrite everything. 1.435 + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 1.436 + OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status); 1.437 + 1.438 + // Set up the viewport 1.439 + const GLint x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x; 1.440 + const GLint y = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.y; // Note that GL uses bottom-up coordinates. 1.441 + const GLsizei w = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.w; 1.442 + const GLsizei h = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.h; 1.443 + glViewport(x, y, w, h); 1.444 + 1.445 + // Set fixed-function render states. 1.446 + //glDepthRange(0.0, 1.0); // This is the default 1.447 + glDepthMask(GL_FALSE); 1.448 + glDisable(GL_DEPTH_TEST); 1.449 + glFrontFace(GL_CW); 1.450 + glEnable(GL_BLEND); 1.451 + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1.452 + 1.453 + // Enable the buffer and shaders we use. 1.454 + ShaderFill fill(pShaderSet); 1.455 + if (pTexture) 1.456 + fill.SetTexture(0, pTexture); 1.457 + 1.458 + // Set shader uniforms. 1.459 + const float scale = HSWDISPLAY_SCALE * ((RenderState.OurHMDInfo.HmdType == HmdType_DK1) ? 0.70f : 1.f); 1.460 + 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. 1.461 + pShaderSet->SetUniform2f("PositionOffset", OrthoProjection[eye].GetTranslation().x, 0.0f); 1.462 + 1.463 + // Set vertex attributes 1.464 + if (GLE_ARB_vertex_array_object) 1.465 + { 1.466 + OVR_ASSERT(VAO != 0); 1.467 + glBindVertexArray(VAO); 1.468 + } 1.469 + 1.470 + if(!VAOInitialized) // This executes for the case that VAO isn't supported. 1.471 + { 1.472 + glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); // This must be called before glVertexAttribPointer is called below. 1.473 + 1.474 + const GLuint shaderProgram = pShaderSet->Prog; 1.475 + GLint attributeLocationArray[3]; 1.476 + 1.477 + attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position"); 1.478 + glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos))); 1.479 + 1.480 + attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color"); 1.481 + glVertexAttribPointer(attributeLocationArray[1], sizeof(Color)/sizeof(uint8_t), GL_UNSIGNED_BYTE, true, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, C))); // True because we want it to convert [0,255] to [0,1] for us. 1.482 + 1.483 + attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord"); 1.484 + glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U))); 1.485 + 1.486 + for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) 1.487 + glEnableVertexAttribArray((GLuint)i); 1.488 + } 1.489 + 1.490 + fill.Set(Prim_TriangleStrip); 1.491 + 1.492 + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 1.493 + 1.494 + if (GLE_ARB_vertex_array_object) 1.495 + { 1.496 + VAOInitialized = true; 1.497 + glBindVertexArray(0); 1.498 + } 1.499 + } 1.500 +} 1.501 + 1.502 + 1.503 +}}} // namespace OVR::CAPI::GL