void atInitialize_01_draw_texture(void** ppAppData, const char* const szArgs, const char* const szBasePath) { GLint iCropRect[4] = {0, 0, 0, 0}; const GLchar* szExtensions = (const GLchar*)glGetString(GL_EXTENSIONS); const char* filename; GLuint texture = 0; GLenum target; GLboolean isMipmapped; GLboolean npotTexture; GLenum glerror; GLubyte* pKvData; GLuint kvDataLen; KTX_dimensions dimensions; KTX_error_code ktxerror; KTX_hash_table kvtable; GLint sign_s = 1, sign_t = 1; DrawTexture* pData = (DrawTexture*)atMalloc(sizeof(DrawTexture), 0); atAssert(pData); atAssert(ppAppData); *ppAppData = pData; pData->bInitialized = GL_FALSE; pData->gnTexture = 0; if (strstr(szExtensions, "OES_draw_texture") != NULL) { pData->glDrawTexsOES = (PFNGLDRAWTEXSOESPROC)SDL_GL_GetProcAddress("glDrawTexsOES"); pData->glDrawTexiOES = (PFNGLDRAWTEXIOESPROC)SDL_GL_GetProcAddress("glDrawTexiOES"); pData->glDrawTexxOES = (PFNGLDRAWTEXXOESPROC)SDL_GL_GetProcAddress("glDrawTexxOES"); pData->glDrawTexfOES = (PFNGLDRAWTEXFOESPROC)SDL_GL_GetProcAddress("glDrawTexfOES"); pData->glDrawTexsvOES = (PFNGLDRAWTEXSVOESPROC)SDL_GL_GetProcAddress("glDrawTexsvOES"); pData->glDrawTexivOES = (PFNGLDRAWTEXIVOESPROC)SDL_GL_GetProcAddress("glDrawTexivOES"); pData->glDrawTexxvOES = (PFNGLDRAWTEXXVOESPROC)SDL_GL_GetProcAddress("glDrawTexxvOES"); pData->glDrawTexfvOES = (PFNGLDRAWTEXFVOESPROC)SDL_GL_GetProcAddress("glDrawTexfvOES"); } else { /* Can't do anything */ atMessageBox("This OpenGL ES implementation does not support " "OES_draw_texture.", "Can't Run Test", AT_MB_OK|AT_MB_ICONERROR); return; } if (strstr(szExtensions, "OES_texture_npot") != NULL) pData->bNpotSupported = GL_TRUE; else pData->bNpotSupported = GL_FALSE; if ((filename = strchr(szArgs, ' ')) != NULL) { if (!strncmp(szArgs, "--npot ", 7)) { npotTexture = GL_TRUE; #if defined(DEBUG) } else { assert(0); /* Unknown argument in sampleInvocations */ #endif } } else { filename = szArgs; npotTexture = GL_FALSE; } if (npotTexture && !pData->bNpotSupported) { /* Load error texture. */ filename = "testimages/no-npot.ktx"; } filename = atStrCat(szBasePath, filename); if (filename != NULL) { ktxerror = ktxLoadTextureN(filename, &pData->gnTexture, &target, &dimensions, &isMipmapped, &glerror, &kvDataLen, &pKvData); if (KTX_SUCCESS == ktxerror) { if (target != GL_TEXTURE_2D) { /* Can only draw 2D textures */ glDeleteTextures(1, &pData->gnTexture); return; } ktxerror = ktxHashTable_Deserialize(kvDataLen, pKvData, &kvtable); if (KTX_SUCCESS == ktxerror) { GLchar* pValue; GLuint valueLen; if (KTX_SUCCESS == ktxHashTable_FindValue(kvtable, KTX_ORIENTATION_KEY, &valueLen, (void*)&pValue)) { char s, t; if (sscanf(pValue, /*valueLen,*/ KTX_ORIENTATION2_FMT, &s, &t) == 2) { if (s == 'l') sign_s = -1; if (t == 'd') sign_t = -1; } } ktxHashTable_Destroy(kvtable); free(pKvData); } iCropRect[2] = pData->iTexWidth = dimensions.width; iCropRect[3] = pData->iTexHeight = dimensions.height; iCropRect[2] *= sign_s; iCropRect[3] *= sign_t; glEnable(target); if (isMipmapped) /* Enable bilinear mipmapping */ /* TO DO: application can consider inserting a key,value pair in the KTX * that indicates what type of filtering to use. */ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); else glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexParameteriv(target, GL_TEXTURE_CROP_RECT_OES, iCropRect); /* Check for any errors */ glerror = glGetError(); } else { char message[1024]; int maxchars = sizeof(message)/sizeof(char); int nchars; nchars = snprintf(message, maxchars, "Load of texture \"%s\" failed: ", filename); maxchars -= nchars; if (ktxerror == KTX_GL_ERROR) { nchars += snprintf(&message[nchars], maxchars, "GL error %#x occurred.", glerror); } else { nchars += snprintf(&message[nchars], maxchars, "%s.", ktxErrorString(ktxerror)); } atMessageBox(message, "Texture load failed", AT_MB_OK | AT_MB_ICONERROR); pData->iTexWidth = pData->iTexHeight = 50; pData->gnTexture = 0; } atFree((void*)filename, NULL); } /* else Out of memory. In which case, a message box is unlikely to work. */ glClearColor(0.4f, 0.4f, 0.5f, 1.0f); glColor4f(1.0f, 1.0f, 0.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_BYTE, 0, (GLvoid *)frame_position); pData->bInitialized = GL_TRUE; }
void atInitialize_02_cube(void** ppAppData, const char* const args) { GLuint texture = 0; GLenum target; GLenum glerror; GLboolean isMipmapped; GLuint gnDecalFs, gnVs; GLsizeiptr offset; KTX_error_code ktxerror; CubeTextured* pData = (CubeTextured*)atMalloc(sizeof(CubeTextured), 0); atAssert(pData); atAssert(ppAppData); *ppAppData = pData; pData->bInitialized = GL_FALSE; pData->gnTexture = 0; ktxerror = ktxLoadTextureN(args, &texture, &target, NULL, &isMipmapped, &glerror, 0, NULL); if (KTX_SUCCESS == ktxerror) { if (target != GL_TEXTURE_2D) { /* Can only draw 2D textures */ glDeleteTextures(1, &texture); return; } if (isMipmapped) /* Enable bilinear mipmapping */ /* TO DO: application can consider inserting a key,value pair in the KTX * file that indicates what type of filtering to use. */ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); else glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); atAssert(GL_NO_ERROR == glGetError()); } else { char message[1024]; int maxchars = sizeof(message)/sizeof(char); int nchars; nchars = snprintf(message, maxchars, "Load of texture \"%s\" failed: %s.", args, ktxErrorString(ktxerror)); if (ktxerror == KTX_GL_ERROR) { maxchars -= nchars; nchars += snprintf(&message[nchars], maxchars, " GL error is %#x.", glerror); } atMessageBox(message, "Texture load failed", AT_MB_OK|AT_MB_ICONERROR); } /* By default dithering is enabled. Dithering does not provide visual improvement * in this sample so disable it to improve performance. */ glDisable(GL_DITHER); glEnable(GL_CULL_FACE); glClearColor(0.2f,0.3f,0.4f,1.0f); // Create a VAO and bind it. glGenVertexArrays(1, &pData->gnVao); glBindVertexArray(pData->gnVao); // Must have vertex data in buffer objects to use VAO's on ES3/GL Core glGenBuffers(1, &pData->gnVbo); glBindBuffer(GL_ARRAY_BUFFER, pData->gnVbo); // Must be done after the VAO is bound // Use the same buffer for vertex attributes and element indices. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pData->gnVbo); // Create the buffer data store. glBufferData(GL_ARRAY_BUFFER, sizeof(cube_face) + sizeof(cube_color) + sizeof(cube_texture) + sizeof(cube_normal) + sizeof(cube_index_buffer), NULL, GL_STATIC_DRAW); // Interleave data copying and attrib pointer setup so offset is only computed once. glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); offset = 0; glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_face), cube_face); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(cube_face); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_color), cube_color); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(cube_color); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_texture), cube_texture); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(cube_texture); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_normal), cube_normal); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(cube_normal); pData->iIndicesOffset = offset; // Either of the following can be used to buffer the data. glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_index_buffer), cube_index_buffer); //glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, sizeof(cube_index_buffer), cube_index_buffer); if (makeShader(GL_VERTEX_SHADER, pszVs, &gnVs)) { if (makeShader(GL_FRAGMENT_SHADER, pszDecalFs, &gnDecalFs)) { if (makeProgram(gnVs, gnDecalFs, &pData->gnTexProg)) { pData->gulMvMatrixLocTP = glGetUniformLocation(pData->gnTexProg, "mvmatrix"); pData->gulPMatrixLocTP = glGetUniformLocation(pData->gnTexProg, "pmatrix"); pData->gulSamplerLocTP = glGetUniformLocation(pData->gnTexProg, "sampler"); glUseProgram(pData->gnTexProg); // We're using the default texture unit 0 glUniform1i(pData->gulSamplerLocTP, 0); } } glDeleteShader(gnVs); glDeleteShader(gnDecalFs); } atAssert(GL_NO_ERROR == glGetError()); pData->bInitialized = GL_TRUE; }
DrawTexture::DrawTexture(uint32_t width, uint32_t height, const char* const szArgs, const std::string sBasePath) : LoadTestSample(width, height, sBasePath) { GLint iCropRect[4] = {0, 0, 0, 0}; const GLchar* szExtensions = (const GLchar*)glGetString(GL_EXTENSIONS); const char* filename; std::string pathname; GLenum target; GLboolean isMipmapped; GLboolean npotTexture; GLenum glerror; GLubyte* pKvData; GLuint kvDataLen; KTX_dimensions dimensions; KTX_error_code ktxresult; KTX_hash_table kvtable; GLint sign_s = 1, sign_t = 1; bInitialized = false; gnTexture = 0; typedef void (*PFN_voidFunction)(void); if (strstr(szExtensions, "OES_draw_texture") != NULL) { /* * This strange casting is because SDL_GL_GetProcAddress returns a * void* thus is not compatible with ISO C which forbids conversion * of object pointers to function pointers. The cast masks the * conversion from the compiler thus no warning even though -pedantic * is set. Ideally, if SDL_GL_GetPRocAddress returned a (void*)(void), * the assignment would be * * glDrawFooOES = (PFNHLDRAWFOOOESPROC)SDL_GetProcAddress(...) */ \ *(void **)(&glDrawTexsOES) = SDL_GL_GetProcAddress("glDrawTexsOES"); *(void **)(&glDrawTexiOES) = SDL_GL_GetProcAddress("glDrawTexiOES"); *(void **)(&glDrawTexxOES) = SDL_GL_GetProcAddress("glDrawTexxOES"); *(void **)(&glDrawTexfOES) = SDL_GL_GetProcAddress("glDrawTexfOES"); *(void **)(&glDrawTexsvOES) = SDL_GL_GetProcAddress("glDrawTexsvOES"); *(void **)(&glDrawTexivOES) = SDL_GL_GetProcAddress("glDrawTexivOES"); *(void **)(&glDrawTexxvOES) = SDL_GL_GetProcAddress("glDrawTexxvOES"); *(void **)(&glDrawTexfvOES) = SDL_GL_GetProcAddress("glDrawTexfvOES"); } else { /* Can't do anything */ std::stringstream message; message << "DrawTexture: this OpenGL ES implementation does not support" << " OES_draw_texture. Can't Run Test"; throw std::runtime_error(message.str()); } if (strstr(szExtensions, "OES_texture_npot") != NULL) bNpotSupported = GL_TRUE; else bNpotSupported = GL_FALSE; if ((filename = strchr(szArgs, ' ')) != NULL) { npotTexture = GL_FALSE; if (!strncmp(szArgs, "--npot ", 7)) { npotTexture = GL_TRUE; #if defined(DEBUG) } else { assert(0); /* Unknown argument in sampleInvocations */ #endif } } else { filename = szArgs; npotTexture = GL_FALSE; } if (npotTexture && !bNpotSupported) { /* Load error texture. */ filename = "testimages/no-npot.ktx"; } pathname = getAssetPath() + filename; ktxresult = ktxLoadTextureN(pathname.c_str(), &gnTexture, &target, &dimensions, &isMipmapped, &glerror, &kvDataLen, &pKvData); if (KTX_SUCCESS == ktxresult) { if (target != GL_TEXTURE_2D) { /* Can only draw 2D textures */ glDeleteTextures(1, &gnTexture); return; } ktxresult = ktxHashTable_Deserialize(kvDataLen, pKvData, &kvtable); if (KTX_SUCCESS == ktxresult) { GLchar* pValue; GLuint valueLen; if (KTX_SUCCESS == ktxHashTable_FindValue(kvtable, KTX_ORIENTATION_KEY, &valueLen, (void**)&pValue)) { char s, t; if (sscanf(pValue, /*valueLen,*/ KTX_ORIENTATION2_FMT, &s, &t) == 2) { if (s == 'l') sign_s = -1; if (t == 'd') sign_t = -1; } } ktxHashTable_Destroy(kvtable); free(pKvData); } iCropRect[2] = uTexWidth = dimensions.width; iCropRect[3] = uTexHeight = dimensions.height; iCropRect[2] *= sign_s; iCropRect[3] *= sign_t; glEnable(target); if (isMipmapped) // Enable bilinear mipmapping. // TO DO: application can consider inserting a key,value pair in // the KTX file that indicates what type of filtering to use. glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); else glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexParameteriv(target, GL_TEXTURE_CROP_RECT_OES, iCropRect); /* Check for any errors */ assert(GL_NO_ERROR == glGetError()); } else { std::stringstream message; message << "Load of texture from \"" << pathname << "\" failed: "; if (ktxresult == KTX_GL_ERROR) { message << std::showbase << "GL error " << std::hex << glerror << "occurred."; } else { message << ktxErrorString(ktxresult); } throw std::runtime_error(message.str()); } glClearColor(0.4f, 0.4f, 0.5f, 1.0f); glColor4f(1.0f, 1.0f, 0.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_BYTE, 0, (GLvoid *)frame_position); bInitialized = GL_TRUE; }
DrawTexture::DrawTexture(uint32_t width, uint32_t height, const char* const szArgs, const std::string sBasePath) : GL3LoadTestSample(width, height, szArgs, sBasePath) { std::string filename; GLfloat* pfQuadTexCoords = quad_texture; GLfloat fTmpTexCoords[sizeof(quad_texture)/sizeof(GLfloat)]; GLenum target; GLboolean isMipmapped; GLenum glerror; GLubyte* pKvData; GLuint kvDataLen; GLint sign_s = 1, sign_t = 1; GLint i; GLuint gnColorFs, gnDecalFs, gnVs; GLsizeiptr offset; KTX_dimensions dimensions; KTX_error_code ktxresult; KTX_hash_table kvtable; bInitialized = false; gnTexture = 0; filename = getAssetPath() + szArgs; ktxresult = ktxLoadTextureN(filename.c_str(), &gnTexture, &target, &dimensions, &isMipmapped, &glerror, &kvDataLen, &pKvData); if (KTX_SUCCESS == ktxresult) { ktxresult = ktxHashTable_Deserialize(kvDataLen, pKvData, &kvtable); if (KTX_SUCCESS == ktxresult) { GLchar* pValue; GLuint valueLen; if (KTX_SUCCESS == ktxHashTable_FindValue(kvtable, KTX_ORIENTATION_KEY, &valueLen, (void**)&pValue)) { char s, t; if (sscanf(pValue, /*valueLen,*/ KTX_ORIENTATION2_FMT, &s, &t) == 2) { if (s == 'l') sign_s = -1; if (t == 'd') sign_t = -1; } } ktxHashTable_Destroy(kvtable); free(pKvData); } if (sign_s < 0 || sign_t < 0) { // Transform the texture coordinates to get correct image // orientation. int iNumCoords = sizeof(quad_texture) / sizeof(float); for (i = 0; i < iNumCoords; i++) { fTmpTexCoords[i] = quad_texture[i]; if (i & 1) { // odd, i.e. a y coordinate if (sign_t < 1) { fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1; } } else { // an x coordinate if (sign_s < 1) { fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1; } } } pfQuadTexCoords = fTmpTexCoords; } uTexWidth = dimensions.width; uTexHeight = dimensions.height; if (isMipmapped) // Enable bilinear mipmapping. // TO DO: application can consider inserting a key,value pair in // the KTX file that indicates what type of filtering to use. glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); else glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); assert(GL_NO_ERROR == glGetError()); } else { std::stringstream message; message << "Load of texture from \"" << filename << "\" failed: "; if (ktxresult == KTX_GL_ERROR) { message << std::showbase << "GL error " << std::hex << glerror << " occurred."; } else { message << ktxErrorString(ktxresult); } throw std::runtime_error(message.str()); } glClearColor(0.4f, 0.4f, 0.5f, 1.0f); // Must have vertex data in buffer objects to use VAO's on ES3/GL Core glGenBuffers(1, &gnVbo); glBindBuffer(GL_ARRAY_BUFFER, gnVbo); // Create the buffer data store glBufferData(GL_ARRAY_BUFFER, sizeof(frame_position) + sizeof(frame_color) + sizeof(quad_position) + sizeof(quad_color) + sizeof(quad_texture), NULL, GL_STATIC_DRAW); glGenVertexArrays(2, gnVaos); // Interleave data copying and attrib pointer setup so offset is only // computed once. // Setup VAO and buffer the data for frame glBindVertexArray(gnVaos[FRAME]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); offset = 0; glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(frame_position), frame_position); glVertexAttribPointer(0, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(frame_position); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(frame_color), frame_color); glVertexAttribPointer(1, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(frame_color); // Setup VAO for quad glBindVertexArray(gnVaos[QUAD]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_position), quad_position); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(quad_position); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_color), quad_color); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(quad_color); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_texture), pfQuadTexCoords); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); glBindVertexArray(0); try { makeShader(GL_VERTEX_SHADER, pszVs, &gnVs); makeShader(GL_FRAGMENT_SHADER, pszColorFs, &gnColorFs); makeProgram(gnVs, gnColorFs, &gnColProg); gulMvMatrixLocCP = glGetUniformLocation(gnColProg, "mvmatrix"); gulPMatrixLocCP = glGetUniformLocation(gnColProg, "pmatrix"); makeShader(GL_FRAGMENT_SHADER, pszDecalFs, &gnDecalFs); makeProgram(gnVs, gnDecalFs, &gnTexProg); } catch (std::exception& e) { (void)e; // To quiet unused variable warnings from some compilers. throw; } gulMvMatrixLocTP = glGetUniformLocation(gnTexProg, "mvmatrix"); gulPMatrixLocTP = glGetUniformLocation(gnTexProg, "pmatrix"); gulSamplerLocTP = glGetUniformLocation(gnTexProg, "sampler"); glUseProgram(gnTexProg); // We're using the default texture unit 0 glUniform1i(gulSamplerLocTP, 0); glDeleteShader(gnVs); glDeleteShader(gnColorFs); glDeleteShader(gnDecalFs); // Set the quad's mv matrix to scale by the texture size. // With the pixel-mapping ortho projection set below, the texture will // be rendered at actual size just like DrawTex*OES. quadMvMatrix = glm::scale(glm::mat4(), glm::vec3((float)uTexWidth / 2, (float)uTexHeight / 2, 1)); assert(GL_NO_ERROR == glGetError()); bInitialized = true; }
void atInitialize_01_draw_texture(void** ppAppData, const char* const szArgs, const char* const szBasePath) { const char* filename; GLfloat* pfQuadTexCoords = quad_texture; GLfloat fTmpTexCoords[sizeof(quad_texture)/sizeof(GLfloat)]; GLuint texture = 0; GLenum target; GLboolean isMipmapped; GLenum glerror; GLubyte* pKvData; GLuint kvDataLen; GLint sign_s = 1, sign_t = 1; GLint i; GLuint gnColorFs, gnDecalFs, gnVs; GLsizeiptr offset; KTX_dimensions dimensions; KTX_error_code ktxerror; KTX_hash_table kvtable; DrawTexture* pData = (DrawTexture*)atMalloc(sizeof(DrawTexture), 0); atAssert(pData); atAssert(ppAppData); *ppAppData = pData; pData->bInitialized = GL_FALSE; pData->gnTexture = 0; filename = atStrCat(szBasePath, szArgs); if (filename != NULL) { ktxerror = ktxLoadTextureN(filename, &pData->gnTexture, &target, &dimensions, &isMipmapped, &glerror, &kvDataLen, &pKvData); if (KTX_SUCCESS == ktxerror) { ktxerror = ktxHashTable_Deserialize(kvDataLen, pKvData, &kvtable); if (KTX_SUCCESS == ktxerror) { GLchar* pValue; GLuint valueLen; if (KTX_SUCCESS == ktxHashTable_FindValue(kvtable, KTX_ORIENTATION_KEY, &valueLen, (void**)&pValue)) { char s, t; if (sscanf(pValue, /*valueLen,*/ KTX_ORIENTATION2_FMT, &s, &t) == 2) { if (s == 'l') sign_s = -1; if (t == 'd') sign_t = -1; } } ktxHashTable_Destroy(kvtable); free(pKvData); } if (sign_s < 0 || sign_t < 0) { // Transform the texture coordinates to get correct image orientation. int iNumCoords = sizeof(quad_texture) / sizeof(float); for (i = 0; i < iNumCoords; i++) { fTmpTexCoords[i] = quad_texture[i]; if (i & 1) { // odd, i.e. a y coordinate if (sign_t < 1) { fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1; } } else { // an x coordinate if (sign_s < 1) { fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1; } } } pfQuadTexCoords = fTmpTexCoords; } pData->iTexWidth = dimensions.width; pData->iTexHeight = dimensions.height; if (isMipmapped) /* Enable bilinear mipmapping */ /* TO DO: application can consider inserting a key,value pair in the KTX * that indicates what type of filtering to use. */ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); else glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); atAssert(GL_NO_ERROR == glGetError()); } else { char message[1024]; int maxchars = sizeof(message)/sizeof(char); int nchars; nchars = snprintf(message, maxchars, "Load of texture \"%s\" failed: ", filename); maxchars -= nchars; if (ktxerror == KTX_GL_ERROR) { nchars += snprintf(&message[nchars], maxchars, "GL error %#x occurred.", glerror); } else { nchars += snprintf(&message[nchars], maxchars, "%s.", ktxErrorString(ktxerror)); } atMessageBox(message, "Texture load failed", AT_MB_OK|AT_MB_ICONERROR); pData->iTexWidth = pData->iTexHeight = 50; pData->gnTexture = 0; } atFree((void*)filename, NULL); } /* else Out of memory. In which case, a message box is unlikely to work. */ glClearColor(0.4f, 0.4f, 0.5f, 1.0f); // Must have vertex data in buffer objects to use VAO's on ES3/GL Core glGenBuffers(1, &pData->gnVbo); glBindBuffer(GL_ARRAY_BUFFER, pData->gnVbo); // Create the buffer data store glBufferData(GL_ARRAY_BUFFER, sizeof(frame_position) + sizeof(frame_color) + sizeof(quad_position) + sizeof(quad_color) + sizeof(quad_texture), NULL, GL_STATIC_DRAW); glGenVertexArrays(2, pData->gnVaos); // Interleave data copying and attrib pointer setup so offset is only computed once. // Setup VAO and buffer the data for frame glBindVertexArray(pData->gnVaos[FRAME]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); offset = 0; glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(frame_position), frame_position); glVertexAttribPointer(0, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(frame_position); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(frame_color), frame_color); glVertexAttribPointer(1, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(frame_color); // Setup VAO for quad glBindVertexArray(pData->gnVaos[QUAD]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_position), quad_position); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(quad_position); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_color), quad_color); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); offset += sizeof(quad_color); glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_texture), pfQuadTexCoords); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset); glBindVertexArray(0); if (makeShader(GL_VERTEX_SHADER, pszVs, &gnVs)) { if (makeShader(GL_FRAGMENT_SHADER, pszColorFs, &gnColorFs)) { if (makeProgram(gnVs, gnColorFs, &pData->gnColProg)) { pData->gulMvMatrixLocCP = glGetUniformLocation(pData->gnColProg, "mvmatrix"); pData->gulPMatrixLocCP = glGetUniformLocation(pData->gnColProg, "pmatrix"); } } if (makeShader(GL_FRAGMENT_SHADER, pszDecalFs, &gnDecalFs)) { if (makeProgram(gnVs, gnDecalFs, &pData->gnTexProg)) { pData->gulMvMatrixLocTP = glGetUniformLocation(pData->gnTexProg, "mvmatrix"); pData->gulPMatrixLocTP = glGetUniformLocation(pData->gnTexProg, "pmatrix"); pData->gulSamplerLocTP = glGetUniformLocation(pData->gnTexProg, "sampler"); glUseProgram(pData->gnTexProg); // We're using the default texture unit 0 glUniform1i(pData->gulSamplerLocTP, 0); } } glDeleteShader(gnVs); glDeleteShader(gnColorFs); glDeleteShader(gnDecalFs); } // Set the quad's mv matrix to scale by the texture size. // With the pixel-mapping ortho projection set below, the texture will // be rendered at actual size just like DrawTex*OES. for (i = 0; i < 16; i++) { pData->fQuadMvMatrix[i] = atIdentity[i]; pData->fFrameMvMatrix[i] = atIdentity[i]; } pData->fQuadMvMatrix[0*4 + 0] = (float)pData->iTexWidth / 2; pData->fQuadMvMatrix[1*4 + 1] = (float)pData->iTexHeight / 2; atAssert(GL_NO_ERROR == glGetError()); pData->bInitialized = GL_TRUE; }