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; }
int _tmain(int argc, _TCHAR* argv[]) { FILE* f; KTX_error_code ret; KTX_texture_info tinfo; KTX_image_info* infiles; struct commandOptions options; unsigned int imageSize; int exitCode = 0, face, i; unsigned int levelWidth, levelHeight; FileResult readResult; boolean useStdin = false; unsigned char* kvData = NULL; unsigned int kvDataLen = 0; processCommandLine(argc, argv, options); infiles = new KTX_image_info[options.numInputFiles]; if (options.cubemap) tinfo.numberOfFaces = 6; else tinfo.numberOfFaces = 1; // TO DO: handle array textures tinfo.numberOfArrayElements = 0; // TO DO: handle 3D textures. Concatenate the files here or in WriteKTXF? if (!options.lower_left_maps_to_s0t0) { // Non-standard orientation. Add metadata. KTX_hash_table ht = ktxHashTable_Create(); char orientation[10]; assert(strlen(KTX_ORIENTATION2_FMT) < sizeof(orientation)); _snprintf(orientation, sizeof(orientation), KTX_ORIENTATION2_FMT, 'r', 'd'); ktxHashTable_AddKVPair(ht, KTX_ORIENTATION_KEY, strlen(orientation) + 1, orientation); if (KTX_SUCCESS != ktxHashTable_Serialize(ht, &kvDataLen, &kvData)) { fprintf(stderr, "%s: Out of memory\n", options.appName); exit(2); } ktxHashTable_Destroy(ht); } for (i = 0; i < options.numInputFiles; i++) { _TCHAR* infile; if (options.useStdin) { infile = 0; f = stdin; if (WIN32) { /* Set "stdin" to have binary mode */ (void)_setmode( _fileno( stdin ), _O_BINARY ); } } else { infile = argv[options.firstInfileIndex + i]; f = fopen(infile,"rb"); } if (f) { unsigned int w, h, components, componentSize; unsigned char* srcImg; readResult = readNPBM(f, w, h, components, componentSize, imageSize, srcImg); if (SUCCESS == readResult) { /* Sanity check. */ assert(w * h * componentSize * components == imageSize); #if IMAGE_DEBUG dumpImage(infile, w, h, components, componentSize, options.luminance, srcImg); #endif if (h > 1 && options.lower_left_maps_to_s0t0) yflip(srcImg, imageSize, w, h, components*componentSize); infiles[i].size = imageSize; infiles[i].data = srcImg; if (i == 0) { if (componentSize == 1) { tinfo.glType = GL_UNSIGNED_BYTE; tinfo.glTypeSize = 1; } else { tinfo.glType = GL_UNSIGNED_SHORT; tinfo.glTypeSize = 2; } switch (components) { case 1: if (options.luminance) { tinfo.glFormat = tinfo.glBaseInternalFormat = GL_LUMINANCE; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_LUMINANCE8 : GL_LUMINANCE16; else tinfo.glInternalFormat = GL_LUMINANCE; } else if (options.alpha) { tinfo.glFormat = tinfo.glBaseInternalFormat = GL_ALPHA; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_ALPHA8 : GL_ALPHA16; else tinfo.glInternalFormat = GL_ALPHA; } else { tinfo.glFormat = tinfo.glBaseInternalFormat = GL_RED; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_R8 : GL_R16; else tinfo.glInternalFormat = GL_RED; } break; case 2: if (options.luminance) { tinfo.glFormat = tinfo.glBaseInternalFormat = GL_LUMINANCE_ALPHA; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE16_ALPHA16; else tinfo.glInternalFormat = GL_LUMINANCE_ALPHA; } else { tinfo.glFormat = tinfo.glBaseInternalFormat = GL_RG; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_RG8 : GL_RG16; else tinfo.glInternalFormat = GL_RG; } break; case 3: tinfo.glFormat = tinfo.glBaseInternalFormat = GL_RGB; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_RGB8 : GL_RGB16; else tinfo.glInternalFormat = GL_RGB; break; case 4: tinfo.glFormat = tinfo.glBaseInternalFormat = GL_RGBA; if (options.sized) tinfo.glInternalFormat = componentSize == 1 ? GL_RGBA8 : GL_RGBA16; else tinfo.glInternalFormat = GL_RGBA; break; break; default: /* If we get here there's a bug in readPAM */ assert(0); } tinfo.pixelWidth = levelWidth = w; tinfo.pixelHeight = levelHeight = h; tinfo.pixelDepth = 0; if (h == 1 && kvData != NULL) { /* 1D. Don't need orientation metadata */ delete(kvData); kvData = NULL; kvDataLen = 0; } face = tinfo.numberOfFaces; if (options.automipmap) tinfo.numberOfMipmapLevels = 0; else if (options.mipmap) { // Calculate number of miplevels GLuint max_dim = w > h ? w : h; GLint levels = log2(max_dim) + 1; // Check we have enough. if (levels * face > options.numInputFiles) { fprintf(stderr, "%s: not enough input files for %d mipmap levels and faces\n", options.appName, levels); exitCode = 1; goto cleanup2; } tinfo.numberOfMipmapLevels = levels; } else tinfo.numberOfMipmapLevels = 1; } else { if (options.mipmap) { if (face == 0) { levelWidth >>= 1; levelHeight >>= 1; if (w != levelWidth || h != levelHeight) { fprintf(stderr, "%s: \"%s\" has incorrect width or height for current mipmap level\n", options.appName, infile); exitCode = 1; goto cleanup2; } face = options.cubemap ? 6 : 1; } } } face--; if (options.cubemap && w != h && w != levelWidth) { fprintf(stderr, "%s: \"%s,\" intended for a cubemap face, is not square or has incorrect\n" "size for current mipmap level\n", options.appName, infile); exitCode = 1; goto cleanup2; } } else {
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; }