bool StGLStereoFrameBuffer::initLazy(StGLContext& theCtx, const GLsizei theSizeX, const GLsizei theSizeY, const bool theNeedDepthBuffer, const bool theToCompress) { if(isValid() && (theSizeX <= getSizeX() && getSizeX() < theCtx.getMaxTextureSize()) && (theSizeY <= getSizeY() && getSizeY() < theCtx.getMaxTextureSize())) { if(!theToCompress || (((getSizeX() - theSizeX) < 256) && ((getSizeY() - theSizeY) < 256))) { setVPDimensions(theCtx, theSizeX, theSizeY); return true; } } release(theCtx); GLsizei aSizeX = stMax(32, (GLsizei )getAligned(theSizeX, 256)); GLsizei aSizeY = stMax(32, (GLsizei )getAligned(theSizeY, 256)); if(!theCtx.arbNPTW) { StGLFrameBuffer::convertToPowerOfTwo(theCtx, aSizeX, aSizeY); } if(!init(theCtx, aSizeX, aSizeY, theNeedDepthBuffer)) { return false; } theCtx.stglFillBitsFBO(myGLFBufferIds[StGLStereoTexture::LEFT_TEXTURE], aSizeX, aSizeY); ST_DEBUG_LOG("FBO resized to " + aSizeX + " x " + aSizeY + " (for " + theSizeX + " x " + theSizeY + ")"); setVPDimensions(theCtx, theSizeX, theSizeY); return true; }
bool StGLFontEntry::createTexture(StGLContext& theCtx) { const GLint aMaxSize = theCtx.getMaxTextureSize(); GLint aGlyphsNb = 0; if(myFont->hasCJK() || myFont->hasKorean()) { // italic does not make sense for Chinese // limit overall number of glyphs in the single texture to 4k // (single font file might contain about 20k-50k glyphs) aGlyphsNb = stMin(4000, 2 * myFont->getGlyphsNumber() - GLint(myLastTileId) + 1); } else { // western might contain reg/bold/italic/bolditalic styles // limit overall number of glyphs in the single texture to 1k // (single font file might contain about 6k glyphs for different languages) aGlyphsNb = stMin(1000, 4 * myFont->getGlyphsNumber() - GLint(myLastTileId) + 1); } const GLsizei aTextureSizeX = getPowerOfTwo(aGlyphsNb * myTileSizeX, aMaxSize); const size_t aTilesPerRow = aTextureSizeX / myTileSizeX; GLsizei aTextureSizeY = stMin(getEvenNumber(GLint((aGlyphsNb / aTilesPerRow) + 1) * myTileSizeY), aMaxSize); if(!theCtx.arbNPTW) { aTextureSizeY = getPowerOfTwo(aTextureSizeY, aMaxSize); } stMemZero(&myLastTilePx, sizeof(myLastTilePx)); myLastTilePx.bottom() = myTileSizeY; myTextures.add(new StGLTexture(theCtx.arbTexRG ? GL_R8 : GL_ALPHA)); myFbos.add(new StGLFrameBuffer()); StHandle<StGLTexture>& aTexture = myTextures[myTextures.size() - 1]; StHandle<StGLFrameBuffer>& aFbo = myFbos [myTextures.size() - 1]; if(!aTexture->initTrash(theCtx, aTextureSizeX, aTextureSizeY)) { return false; } aTexture->bind(theCtx); theCtx.core11fwd->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); theCtx.core11fwd->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); aTexture->unbind(theCtx); // destruction of temporary FBO produces broken texture on Catalyst drivers for unknown reason //StGLFrameBuffer::clearTexture(theCtx, aTexture); #if !defined(GL_ES_VERSION_2_0) if(theCtx.arbTexClear) { theCtx.core11fwd->glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); theCtx.core11fwd->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); theCtx.core11fwd->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); const stUByte_t THE_BLACK = 0; theCtx.extAll->glClearTexImage(aTexture->getTextureId(), 0, theCtx.arbTexRG ? GL_RED : GL_ALPHA, GL_UNSIGNED_BYTE, &THE_BLACK); } else if(aFbo->init(theCtx, aTexture, false)) { aFbo->clearTexture(theCtx); } else { ST_ERROR_LOG("Fail to bind " + (theCtx.arbTexRG ? "GL_R8" : "GL_ALPHA8") + " texture to FBO!"); } #else (void )aFbo; #endif return true; }
void StGLProjCamera::resize(StGLContext& theCtx, const GLsizei theSizeX, const GLsizei theSizeY) { const GLsizei aSizeY = (theSizeY > 0) ? theSizeY : 1; myAspect = GLfloat(theSizeX) / GLfloat(aSizeY); theCtx.stglResizeViewport(theSizeX, aSizeY); // reset the current viewport updateFrustum(); }
// this function called ONLY from plugin thread bool StGLTextureQueue::stglUpdateStTextures(StGLContext& theCtx) { int aSwapState = swapFBOnReady(theCtx); if(aSwapState == SWAPONREADY_WAITLIM) { return false; } if(!myMutexPop.tryLock()) { return aSwapState == SWAPONREADY_SWAPPED; } // do we already in update cycle? if(!myIsInUpdTexture) { // check event from video thread if(!isEmpty()) { myIsInUpdTexture = true; } } else if(isEmpty()) { // if we in texture update sequence - check queue not emptied! myIsInUpdTexture = false; } // still nothing to update? so return if(!myIsInUpdTexture) { myMutexPop.unlock(); return aSwapState == SWAPONREADY_SWAPPED; } if(!theCtx.isBound() || myDataFront->fillTexture(theCtx, myQTexture)) { myIsReadyToSwap = true; myMutexSize.lock(); myCurrPts = myDataFront->getPTS(); myDataSnap = myDataFront; myNewShotEvent.set(); if(myToCompress) { myDataFront->reset(); } myDataFront = myDataFront->getNext(); ST_DEBUG_ASSERT(myQueueSize != 0); // critical error! --myQueueSize; myMutexSize.unlock(); myIsInUpdTexture = false; } myMutexPop.unlock(); // try early swap const bool isAlreadySwapped = (aSwapState == SWAPONREADY_SWAPPED); aSwapState = swapFBOnReady(theCtx); return (aSwapState == SWAPONREADY_SWAPPED || isAlreadySwapped); }
bool StGLProgram::link(StGLContext& theCtx) { if(!isValid()) { return false; } theCtx.core20fwd->glLinkProgram(myProgramId); // if linkage failed - automatically remove the program! if(!isLinked(theCtx)) { theCtx.pushError(StString("Linking of the program '") + myTitle + "' failed!\n" + getLinkageInfo(theCtx)); release(theCtx); return false; } #ifdef __ST_DEBUG_SHADERS__ const StString anInfo = getLinkageInfo(theCtx); ST_DEBUG_LOG("Program '" + myTitle + "' has been linked" + (!anInfo.isEmpty() ? (StString(". Log:\n") + anInfo) : (StString()))); #endif return true; }
void StGLFrameTextures::increaseSize(StGLContext& theCtx, StGLFrameTexture& theTexture, const GLsizei theTextureSizeX, const GLsizei theTextureSizeY) { // test existing size / new size /// TODO (Kirill Gavrilov#8) we can automatically reduce texture size here if((theTexture.getSizeX() < theTextureSizeX) || (theTexture.getSizeY() < theTextureSizeY) || !theTexture.isValid()) { ST_DEBUG_LOG("Requested texture size (" + theTextureSizeX + 'x' + theTextureSizeY + ") larger than current texture size(" + theTexture.getSizeX() + 'x' + theTexture.getSizeY() + ')'); const GLsizei anOriginalSizeX = theTexture.getSizeX(); const GLsizei anOriginalSizeY = theTexture.getSizeY(); const GLint aMaxTexDim = theCtx.getMaxTextureSize(); GLsizei aNewSizeX = stMin(theTextureSizeX, GLsizei(aMaxTexDim)); GLsizei aNewSizeY = stMin(theTextureSizeY, GLsizei(aMaxTexDim)); if(!theCtx.arbNPTW) { aNewSizeX = getPowerOfTwo(theTextureSizeX, GLsizei(aMaxTexDim)); aNewSizeY = getPowerOfTwo(theTextureSizeY, GLsizei(aMaxTexDim)); } if((aNewSizeY != anOriginalSizeY) || (aNewSizeX != anOriginalSizeX)) { if(!theTexture.initTrash(theCtx, aNewSizeX, aNewSizeY)) { theTexture.initTrash(theCtx, (anOriginalSizeX > 0) ? anOriginalSizeX : 512, (anOriginalSizeY > 0) ? anOriginalSizeY : 512); ST_DEBUG_LOG("FAILED to Increase the texture size to (" + aNewSizeX + 'x' + aNewSizeY + ")!"); } else { ST_DEBUG_LOG("Increase the texture size to (" + aNewSizeX + 'x' + aNewSizeY + ") success!"); } } else { ST_DEBUG_LOG("Not possible to Increase the texture size!"); } } }
StWinHandles::StWinHandles() #ifdef _WIN32 : ThreadWnd(0), EventMsgThread(true), hWindow(NULL), hWindowGl(NULL), hWinTmp(NULL), myMKeyStop(GlobalAddAtom(MAKEINTATOM(VK_MEDIA_STOP))), myMKeyPlay(GlobalAddAtom(MAKEINTATOM(VK_MEDIA_PLAY_PAUSE))), myMKeyPrev(GlobalAddAtom(MAKEINTATOM(VK_MEDIA_PREV_TRACK))), myMKeyNext(GlobalAddAtom(MAKEINTATOM(VK_MEDIA_NEXT_TRACK))), ThreadGL(0), hDC(NULL) { // #elif defined(__linux__) : hWindow(0), hWindowGl(0), stXDisplay(), iconImage(0), iconShape(0), xDNDRequestType(None), xDNDSrcWindow(0), xDNDVersion(0), xrandrEventBase(0), isRecXRandrEvents(false) { // #endif } StWinHandles::~StWinHandles() { close(); } void StWinHandles::glSwap() { #ifdef _WIN32 if(hDC != NULL) { SwapBuffers(hDC); } #elif defined(__linux__) if(!stXDisplay.isNull() && hRC->makeCurrent(hWindowGl)) { // if GL rendering context is bound to another drawable - we got BadMatch error glXSwapBuffers(stXDisplay->hDisplay, hWindowGl); } #endif } bool StWinHandles::glMakeCurrent() { #ifdef _WIN32 if(hDC != NULL && !hRC.isNull()) { return hRC->isCurrent(hDC) || hRC->makeCurrent(hDC); } #elif defined(__linux__) if(!stXDisplay.isNull() && !hRC.isNull()) { return hRC->makeCurrent(hWindowGl); } #endif return false; } /** * Auxiliary macros. */ #define ST_GL_ERROR_CHECK(theTrueCondition, theErrCode, theErrDesc) \ if(!(theTrueCondition)) { \ stError(theErrDesc); \ return theErrCode; \ } int StWinHandles::glCreateContext(StWinHandles* theSlave, const StRectI_t& theRect, const int theDepthSize, const bool theIsQuadStereo, const bool theDebugCtx) { #ifdef _WIN32 ThreadGL = StThread::getCurrentThreadId(); ST_DEBUG_LOG("WinAPI, glCreateContext, ThreadGL= " + ThreadGL + ", ThreadWnd= " + ThreadWnd); hDC = GetDC(hWindowGl); ST_GL_ERROR_CHECK(hDC != NULL, STWIN_ERROR_WIN32_GLDC, "WinAPI, Can't create Master GL Device Context"); if(theSlave != NULL) { theSlave->ThreadGL = ThreadGL; theSlave->hDC = GetDC(theSlave->hWindowGl); ST_GL_ERROR_CHECK(theSlave->hDC != NULL, STWIN_ERROR_WIN32_GLDC, "WinAPI, Can't create Slave GL Device Context"); } PIXELFORMATDESCRIPTOR aPixFrmtDesc = THE_PIXELFRMT_DOUBLE; aPixFrmtDesc.cDepthBits = (BYTE )theDepthSize; if(theIsQuadStereo) { aPixFrmtDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_GDI | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO; } int aPixFrmtId = ChoosePixelFormat(hDC, &aPixFrmtDesc); ST_GL_ERROR_CHECK(aPixFrmtId != 0, STWIN_ERROR_WIN32_PIXELFORMATF, "WinAPI, Can't find a suitable PixelFormat for Master"); if(theSlave != NULL && ChoosePixelFormat(theSlave->hDC, &aPixFrmtDesc) != aPixFrmtId) { ST_ERROR_LOG("Slave window returns another pixel format! Try to ignore..."); } if(theIsQuadStereo) { DescribePixelFormat(hDC, aPixFrmtId, sizeof(PIXELFORMATDESCRIPTOR), &aPixFrmtDesc); if((aPixFrmtDesc.dwFlags & PFD_STEREO) == 0) { ST_ERROR_LOG("WinAPI, Quad Buffered stereo not supported"); } else { //bool isVistaPlus = StSys::isVistaPlus(); //bool isWin8Plus = StSys::isWin8Plus(); ///myNeedsFullscr } } HMODULE aModule = GetModuleHandleW(NULL); hWinTmp = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_NOACTIVATE, ClassTmp.toCString(), L"TmpWnd", WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED, theRect.left() + 2, theRect.top() + 2, 4, 4, NULL, NULL, aModule, NULL); ST_GL_ERROR_CHECK(hWinTmp != NULL, STWIN_ERROR_WIN32_GLDC, "WinAPI, Temporary window creation error"); HDC aDevCtxTmp = GetDC(hWinTmp); ST_GL_ERROR_CHECK(aPixFrmtId != 0, STWIN_ERROR_WIN32_PIXELFORMATF, "WinAPI, Can't find a suitable PixelFormat for Tmp"); ST_GL_ERROR_CHECK(SetPixelFormat(aDevCtxTmp, aPixFrmtId, &aPixFrmtDesc), STWIN_ERROR_WIN32_PIXELFORMATS, "WinAPI, Can't set the PixelFormat for Master"); StWinGlrcH aRendCtxTmp = new StWinGlrc(aDevCtxTmp, NULL); ST_GL_ERROR_CHECK(aRendCtxTmp->isValid(), STWIN_ERROR_WIN32_GLRC_CREATE, "WinAPI, Can't create GL Rendering Context"); ST_GL_ERROR_CHECK(aRendCtxTmp->makeCurrent(aDevCtxTmp), STWIN_ERROR_WIN32_GLRC_ACTIVATE, "WinAPI, Can't activate Tmp GL Rendering Context"); StGLContext aCtx; ST_GL_ERROR_CHECK(aCtx.stglInit(), STWIN_ERROR_WIN32_GLRC_ACTIVATE, "WinAPI, Broken Tmp GL Rendering Context"); if(aCtx.extAll->wglChoosePixelFormatARB != NULL) { const int aPixAttribs[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_STEREO_ARB, theIsQuadStereo ? GL_TRUE : GL_FALSE, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, //WGL_SAMPLE_BUFFERS_ARB, 1, //WGL_SAMPLES_ARB, 8, // WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 WGL_COLOR_BITS_ARB, 24, WGL_DEPTH_BITS_ARB, theDepthSize, WGL_STENCIL_BITS_ARB, 0, 0, 0, }; unsigned int aFrmtsNb = 0; aCtx.extAll->wglChoosePixelFormatARB(hDC, aPixAttribs, NULL, 1, &aPixFrmtId, &aFrmtsNb); } ST_GL_ERROR_CHECK(SetPixelFormat(hDC, aPixFrmtId, &aPixFrmtDesc), STWIN_ERROR_WIN32_PIXELFORMATS, "WinAPI, Can't set the PixelFormat for Master"); ST_GL_ERROR_CHECK(theSlave == NULL || SetPixelFormat(theSlave->hDC, aPixFrmtId, &aPixFrmtDesc), STWIN_ERROR_WIN32_PIXELFORMATS, "WinAPI, Can't set the PixelFormat for Slave"); HGLRC aRendCtx = NULL; if(aCtx.extAll->wglCreateContextAttribsARB != NULL) { // Beware! NVIDIA drivers reject context creation when WGL_CONTEXT_PROFILE_MASK_ARB are specified // but not WGL_CONTEXT_MAJOR_VERSION_ARB/WGL_CONTEXT_MINOR_VERSION_ARB int aCtxAttribs[] = { //WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //WGL_CONTEXT_MINOR_VERSION_ARB, 2, //WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, //WGL_CONTEXT_CORE_PROFILE_BIT_ARB, WGL_CONTEXT_FLAGS_ARB, theDebugCtx ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, 0, 0 }; aRendCtx = aCtx.extAll->wglCreateContextAttribsARB(hDC, NULL, aCtxAttribs); } aRendCtxTmp.nullify(); destroyWindow(hWinTmp); hRC = new StWinGlrc(hDC, aRendCtx); ST_GL_ERROR_CHECK(hRC->isValid(), STWIN_ERROR_WIN32_GLRC_CREATE, "WinAPI, Can't create GL Rendering Context"); if(theSlave != NULL) { theSlave->hRC = hRC; } ST_GL_ERROR_CHECK(hRC->makeCurrent(hDC), STWIN_ERROR_WIN32_GLRC_ACTIVATE, "WinAPI, Can't activate Master GL Rendering Context"); return STWIN_INIT_SUCCESS; #elif defined(__linux__) // create an OpenGL rendering context hRC = new StWinGlrc(stXDisplay, theDebugCtx); ST_GL_ERROR_CHECK(hRC->isValid(), STWIN_ERROR_X_GLRC_CREATE, "GLX, could not create rendering context for Master"); if(theSlave != NULL) { theSlave->hRC = hRC; // bind the rendering context to the window ST_GL_ERROR_CHECK(hRC->makeCurrent(theSlave->hWindowGl), STWIN_ERROR_X_GLRC_CREATE, "GLX, Can't activate Slave GL Rendering Context"); } // bind the rendering context to the window ST_GL_ERROR_CHECK(hRC->makeCurrent(hWindowGl), STWIN_ERROR_X_GLRC_CREATE, "GLX, Can't activate Master GL Rendering Context"); return STWIN_INIT_SUCCESS; #endif }
void StGLStereoFrameBuffer::bindBufferRight(StGLContext& theCtx) { theCtx.stglBindFramebuffer(myGLFBufferIds[StGLStereoTexture::RIGHT_TEXTURE]); }
void StGLStereoFrameBuffer::setupViewPort(StGLContext& theCtx) { theCtx.stglResizeViewport(myViewPortX, myViewPortY); }
bool StGLStereoFrameBuffer::init(StGLContext& theCtx, const GLsizei theTextureSizeX, const GLsizei theTextureSizeY, const bool theNeedDepthBuffer) { // release current objects release(theCtx); if(theCtx.arbFbo == NULL) { return false; } // create the textures if(!StGLStereoTexture::initTrash(theCtx, theTextureSizeX, theTextureSizeY)) { release(theCtx); return false; } const GLuint aFboBakDraw = theCtx.stglFramebufferDraw(); const GLuint aFboBakRead = theCtx.stglFramebufferRead(); theCtx.stglBindFramebuffer(StGLFrameBuffer::NO_FRAMEBUFFER); const GLint aDepthFormat = theCtx.isGlGreaterEqual(3, 0) ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT16; if(theNeedDepthBuffer) { theCtx.arbFbo->glGenRenderbuffers(2, myGLDepthRBIds); theCtx.arbFbo->glBindRenderbuffer(GL_RENDERBUFFER, myGLDepthRBIds[StGLStereoTexture::LEFT_TEXTURE]); theCtx.arbFbo->glRenderbufferStorage(GL_RENDERBUFFER, aDepthFormat, theTextureSizeX, theTextureSizeY); } // build FBOs theCtx.arbFbo->glGenFramebuffers(2, myGLFBufferIds); theCtx.stglBindFramebuffer(myGLFBufferIds[StGLStereoTexture::LEFT_TEXTURE]); // bind left texture to left FBO as color buffer theCtx.arbFbo->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, StGLStereoTexture::myTextures[StGLStereoTexture::LEFT_TEXTURE].getTextureId(), 0); if(theNeedDepthBuffer) { // bind left render buffer to left FBO as depth buffer theCtx.arbFbo->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, myGLDepthRBIds[StGLStereoTexture::LEFT_TEXTURE]); } bool isOk = theCtx.arbFbo->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; theCtx.arbFbo->glBindRenderbuffer(GL_RENDERBUFFER, StGLFrameBuffer::NO_RENDERBUFFER); if(!isOk) { release(theCtx); theCtx.stglBindFramebufferDraw(aFboBakDraw); theCtx.stglBindFramebufferRead(aFboBakRead); return false; } theCtx.stglBindFramebuffer(StGLFrameBuffer::NO_FRAMEBUFFER); if(theNeedDepthBuffer) { // create right RenderBuffer (will be used as depth buffer) theCtx.arbFbo->glBindRenderbuffer(GL_RENDERBUFFER, myGLDepthRBIds[StGLStereoTexture::RIGHT_TEXTURE]); theCtx.arbFbo->glRenderbufferStorage(GL_RENDERBUFFER, aDepthFormat, theTextureSizeX, theTextureSizeY); } theCtx.stglBindFramebuffer(myGLFBufferIds[StGLStereoTexture::RIGHT_TEXTURE]); // bind right texture to rights FBO as color buffer theCtx.arbFbo->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, StGLStereoTexture::myTextures[StGLStereoTexture::RIGHT_TEXTURE].getTextureId(), 0); if(theNeedDepthBuffer) { // bind right render buffer to right FBO as depth buffer theCtx.arbFbo->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, myGLDepthRBIds[StGLStereoTexture::RIGHT_TEXTURE]); } isOk = theCtx.arbFbo->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; theCtx.arbFbo->glBindRenderbuffer(GL_RENDERBUFFER, StGLFrameBuffer::NO_RENDERBUFFER); theCtx.stglBindFramebufferDraw(aFboBakDraw); theCtx.stglBindFramebufferRead(aFboBakRead); if(!isOk) { release(theCtx); return false; } ST_DEBUG_LOG("OpenGL, created StFrameBuffer(WxH= " + getSizeX() + 'x' + getSizeY() + ')'); // create vertices buffers to draw simple textured quad StArray<StGLVec4> aQuad(4); aQuad[0] = StGLVec4( 1.0f, -1.0f, 0.0f, 1.0f); // top-right aQuad[1] = StGLVec4( 1.0f, 1.0f, 0.0f, 1.0f); // bottom-right aQuad[2] = StGLVec4(-1.0f, -1.0f, 0.0f, 1.0f); // top-left aQuad[3] = StGLVec4(-1.0f, 1.0f, 0.0f, 1.0f); // bottom-left myVerticesBuf.init(theCtx, aQuad); StArray<StGLVec2> aQuadTC(4); aQuadTC[0] = StGLVec2(1.0f, 0.0f); aQuadTC[1] = StGLVec2(1.0f, 1.0f); aQuadTC[2] = StGLVec2(0.0f, 0.0f); aQuadTC[3] = StGLVec2(0.0f, 1.0f); myTexCoordBuf.init(theCtx, aQuadTC); myViewPortX = theTextureSizeX; myViewPortY = theTextureSizeY; return true; }