Пример #1
0
void atRelease_01_draw_texture (void* pAppData)
{
	DrawTexture* pData = (DrawTexture*)pAppData;
	atAssert(pData);

	if (pData->bInitialized) {
		DrawTexture* pData = (DrawTexture*)pAppData;
		atAssert(pData);
		glDeleteTextures(1, &pData->gnTexture);
	}
	atAssert(GL_NO_ERROR == glGetError());
	atFree(pData, 0);
}
Пример #2
0
void atResize_01_draw_texture (void* pAppData, int iWidth, int iHeight)
{

	DrawTexture* pData = (DrawTexture*)pAppData;
	atAssert(pData);

	glViewport(0, 0, iWidth, iHeight);
	pData->iWidth = iWidth;
	pData->iHeight = iHeight;

	// Set up an orthographic projection where 1 = 1 pixel, and 0,0,0
	// is at the center of the window.
	atSetOrthoZeroAtCenterMatrix(pData->fFramePMatrix, 0.0f, (float)iWidth,
					 0.0, (float)iHeight,
					 -1.0f, 1.0f);

	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(pData->fFramePMatrix);

	glMatrixMode(GL_MODELVIEW);
	// Scale the frame to fill the viewport. To guarantee its lines
	// appear we need to inset them by half-a-pixel hence the -1.
    // [Lines at the edges of the clip volume may or may not appear
	// depending on the OpenGL ES implementation. This is because
	// (a) the edges are on the points of the diamonds of the diamond
	//     exit rule and slight precision errors can easily push the
	//     lines outside the diamonds.
	// (b) the specification allows lines to be up to 1 pixel either
	//     side of the exact position.]
	glLoadIdentity();
	glScalef((float)(iWidth - 1) / 2, (float)(iHeight - 1) / 2, 1);
}
Пример #3
0
void atRun_01_draw_texture (void* pAppData, int iTimeMS)
{
	DrawTexture* pData = (DrawTexture*)pAppData;
	atAssert(pData);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_TEXTURE_2D);
	glDrawArrays(GL_LINE_LOOP, 0, 4);

	glEnable(GL_TEXTURE_2D);
	pData->glDrawTexiOES(pData->iWidth/2 - pData->iTexWidth/2,
		         		 (pData->iHeight)/2 - pData->iTexHeight/2,
						 0,
				 		 pData->iTexWidth, pData->iTexHeight);

	atAssert(GL_NO_ERROR == glGetError());
}
Пример #4
0
void atRelease_02_cube(void* pAppData)
{
	CubeTextured* pData = (CubeTextured*)pAppData;
	atAssert(pData);

	glEnable(GL_DITHER);
	glDisable(GL_CULL_FACE);
	if (pData->bInitialized) {
		glUseProgram(0);
		glDeleteTextures(1, &pData->gnTexture);
		glDeleteProgram(pData->gnTexProg);
		glDeleteBuffers(1, &pData->gnVbo);
		glDeleteVertexArrays(1, &pData->gnVao);
	}
	atAssert(GL_NO_ERROR == glGetError());
	atFree(pData, 0);
}
Пример #5
0
void atResize_02_cube(void* pAppData, int iWidth, int iHeight)
{
	GLfloat matProj[16];
	CubeTextured* pData = (CubeTextured*)pAppData;
	atAssert(pData);

	glViewport(0, 0, iWidth, iHeight);

	atSetProjectionMatrix(matProj, 45.f, iWidth / (GLfloat)iHeight, 1.0f, 100.f);
	glUniformMatrix4fv(pData->gulPMatrixLocTP, 1, GL_FALSE, matProj);
}
Пример #6
0
void atRelease_01_draw_texture (void* pAppData)
{
	DrawTexture* pData = (DrawTexture*)pAppData;
	atAssert(pData);

	if (pData->bInitialized) {
		// A bug in the PVR SDK 3.1 emulator causes the glDeleteProgram(pData->gnColProg)
		// below to raise an INVALID_VALUE error if the following glUseProgram(0)
		// has been executed. Strangely the equivalent line in sample_02_cube_textured.c,
		// where only 1 program is used, does not raise an error.
		glUseProgram(0);
		glDeleteTextures(1, &pData->gnTexture);
		glDeleteProgram(pData->gnTexProg);
		glDeleteProgram(pData->gnColProg);
		glDeleteBuffers(1, &pData->gnVbo);
		glDeleteVertexArrays(2, pData->gnVaos);
	}
	atAssert(GL_NO_ERROR == glGetError());
	atFree(pData, 0);
}
Пример #7
0
void atRun_02_cube(void* pAppData, int iTimeMS) 
{
	/* Draw */
	CubeTextured* pData = (CubeTextured*)pAppData;
	/* Setup the view matrix : just turn around the cube. */
	float matView[16];
	const float fDistance = 50.0f;

	atAssert(pData);

	atSetViewMatrix(matView, 
		(float)cos( iTimeMS*0.001f ) * fDistance, (float)sin( iTimeMS*0.0007f ) * fDistance, (float)sin( iTimeMS*0.001f ) * fDistance, 
		0.0f, 0.0f, 0.0f);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glUniformMatrix4fv(pData->gulMvMatrixLocTP, 1, GL_FALSE, matView);

	glDrawElements(GL_TRIANGLES, sizeof(cube_index_buffer), GL_UNSIGNED_BYTE, (GLvoid*)(pData->iIndicesOffset));

	atAssert(GL_NO_ERROR == glGetError());
}
Пример #8
0
void atRun_01_draw_texture (void* pAppData, int iTimeMS)
{
	DrawTexture* pData = (DrawTexture*)pAppData;
	atAssert(pData);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glBindVertexArray(pData->gnVaos[FRAME]);
	glUseProgram(pData->gnColProg);
	glUniformMatrix4fv(pData->gulMvMatrixLocCP, 1, GL_FALSE, pData->fFrameMvMatrix);
	glUniformMatrix4fv(pData->gulPMatrixLocCP, 1, GL_FALSE, pData->fPMatrix);
	glDrawArrays(GL_LINE_LOOP, 0, 4);

	glBindVertexArray(pData->gnVaos[QUAD]);
	glUseProgram(pData->gnTexProg);
	glUniformMatrix4fv(pData->gulMvMatrixLocTP, 1, GL_FALSE, pData->fQuadMvMatrix);
	glUniformMatrix4fv(pData->gulPMatrixLocTP, 1, GL_FALSE, pData->fPMatrix);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	atAssert(GL_NO_ERROR == glGetError());
}
Пример #9
0
Файл: at.c Проект: Haringat/KTX
/**
 * Select the most appropriate config according to the attributes used as parameter.
 * @param [in] eglDisplay Current display.
 * @param [in] aAttribs List of prefered attributes.
 * @param [out] pResult  Resulting configuration.
 * @return Returns HI_TRUE on success, HI_FALSE on failure.
 */
EGLBoolean
atGetAppropriateEGLConfig(EGLDisplay eglDisplay, const EGLint* aAttribs,
   						  EGLConfig* pResult)
{
    EGLint  i;
    EGLint  iNbConfigs;
    EGLint  iNbChosenConfigs;
    EGLConfig* pConfigs;

    /* No error should have occured until here */
    atAssert(EGL_SUCCESS == eglGetError());
	
	eglGetConfigs(eglDisplay, NULL, 0, &iNbConfigs);
    if ( 0 > iNbConfigs || EGL_SUCCESS != eglGetError()) {
        return 0;
    }

    pConfigs = (EGLConfig*)atMalloc(iNbConfigs * sizeof(EGLConfig), 0);
    if ( 0 == pConfigs ) {
        return EGL_FALSE;
    }

	eglChooseConfig(eglDisplay, aAttribs, pConfigs, iNbConfigs, &iNbChosenConfigs);
    if ( iNbChosenConfigs <= 0 || EGL_SUCCESS != eglGetError() ) {
        atFree(pConfigs, 0);
        return EGL_FALSE;
    }

    /*
     * Select the config in the list.
     */
    {
        EGLint  iNbBits;
        EGLConfig* pSelConfig;
        EGLConfig* pCurConfig;

        /* Initialize Sel values to the worst values so that even if eglGetConfigAttrib function fails, we are not impacted for the test */
        EGLint iSelBufType = 0;    /* Selected buffer type */
        EGLint iSelRGBBits = 0x7fffffff;  /* The selected buffer uses iSelRGBBits bits for the RGB components */
        EGLint iSelABits = 0x7fffffff;  /* The selected buffer uses iSelABits bits for the A components */

        EGLint iCurBufType;      /* Current buffer type */
        EGLint iCurRGBBits;      /* The current buffer uses iCurRGBBits bits for the RGB components */
        EGLint iCurABits;      /* The current buffer uses iCurABits bits for the A components */


        pSelConfig = pConfigs[0];
        eglGetConfigAttrib(eglDisplay, pSelConfig, EGL_COLOR_BUFFER_TYPE, &iSelBufType);
        eglGetConfigAttrib(eglDisplay, pSelConfig, EGL_RED_SIZE, &iNbBits); iSelRGBBits  = iNbBits;
        eglGetConfigAttrib(eglDisplay, pSelConfig, EGL_GREEN_SIZE, &iNbBits); iSelRGBBits += iNbBits;
        eglGetConfigAttrib(eglDisplay, pSelConfig, EGL_BLUE_SIZE, &iNbBits); iSelRGBBits += iNbBits;
        eglGetConfigAttrib(eglDisplay, pSelConfig, EGL_ALPHA_SIZE, &iSelABits);

        for( i = 1; i < iNbChosenConfigs; i++) {
            /* Initialize Cur values to the worst values so that even if eglGetConfigAttrib function fails, we are not impacted for the test */
            iCurBufType = 0;
            iCurRGBBits = 0x7fffffff;
            iCurABits = 0x7fffffff;

            pCurConfig = pConfigs[i];
            eglGetConfigAttrib(eglDisplay, pCurConfig, EGL_COLOR_BUFFER_TYPE, &iCurBufType);
            eglGetConfigAttrib(eglDisplay, pCurConfig, EGL_RED_SIZE, &iNbBits); iCurRGBBits  = iNbBits;
            eglGetConfigAttrib(eglDisplay, pCurConfig, EGL_GREEN_SIZE, &iNbBits); iCurRGBBits += iNbBits;
            eglGetConfigAttrib(eglDisplay, pCurConfig, EGL_BLUE_SIZE, &iNbBits); iCurRGBBits += iNbBits;
            eglGetConfigAttrib(eglDisplay, pCurConfig, EGL_ALPHA_SIZE, &iCurABits);

            if ( ( (EGL_RGB_BUFFER == iCurBufType) && (EGL_RGB_BUFFER != iSelBufType) ) /* TODO: check that EGL_RGB_BUFFER is really what we want */
                 || (iCurRGBBits < iSelRGBBits)
                 || ( (iCurRGBBits == iSelRGBBits) && (iCurABits < iSelABits)))
            {
                pSelConfig = pCurConfig;
                iSelBufType = iCurBufType;
                iSelRGBBits = iCurRGBBits;
                iSelABits = iCurABits;
            }
        }

        atAssert(pSelConfig)
        *pResult = pSelConfig;
    }

    atFree(pConfigs, 0);

    /* EGL might have produced an error but it is handled by the code above, so just flush it, in case */
    eglGetError();

    return EGL_TRUE;
}
Пример #10
0
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;
}
Пример #11
0
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;
}
Пример #12
0
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;
}