#include "CGLCG.h" #include "wsnes9x.h" #include "win32_display.h" #include #ifndef max #define max(a, b) (((a) > (b)) ? (a) : (b)) #endif float npot(float desired) { float out=512.0; while(outcgContext = cgContext; fboFunctionsLoaded = FALSE; ClearPasses(); LoadFBOFunctions(); frameCnt=0; prevTex=0; } CGLCG::~CGLCG(void) { LoadShader(NULL); } void CGLCG::ClearPasses() { /* clean up cg programs, fbos and textures from all regular passes pass 0 is the orignal texture, so ignore that */ if(shaderPasses.size()>1) { for(glPassVector::iterator it=(shaderPasses.begin()+1);it!=shaderPasses.end();it++) { if(it->cgFragmentProgram) cgDestroyProgram(it->cgFragmentProgram); if(it->cgVertexProgram) cgDestroyProgram(it->cgVertexProgram); if(it->fbo) glDeleteFramebuffers(1,&it->fbo); if(it->tex) glDeleteTextures(1,&it->tex); } } for(glLutVector::iterator it=lookupTextures.begin();it!=lookupTextures.end();it++) { if(it->tex) glDeleteTextures(1,&it->tex); } shaderPasses.clear(); lookupTextures.clear(); shaderLoaded = false; } bool CGLCG::LoadFBOFunctions() { if(fboFunctionsLoaded) return true; const char *extensions = (const char *) glGetString(GL_EXTENSIONS); if(extensions && strstr(extensions, "framebuffer_object")) { glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers"); glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers"); glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"); glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D"); glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus"); glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glClientActiveTexture"); if(glGenFramebuffers && glDeleteFramebuffers && glBindFramebuffer && glFramebufferTexture2D && glClientActiveTexture) { fboFunctionsLoaded = true; } } return fboFunctionsLoaded; } void CGLCG::checkForCgError(const char *situation) { char buffer[4096]; CGerror error = cgGetError(); const char *string = cgGetErrorString(error); if (error != CG_NO_ERROR) { sprintf(buffer, "Situation: %s\n" "Error: %s\n\n" "Cg compiler output...\n", situation, string); MessageBoxA(0, buffer, "Cg error", MB_OK|MB_ICONEXCLAMATION); if (error == CG_COMPILER_ERROR) { MessageBoxA(0, cgGetLastListing(cgContext), "Cg compilation error", MB_OK|MB_ICONEXCLAMATION); } } } #define IS_SLASH(x) ((x) == TEXT('\\') || (x) == TEXT('/')) bool CGLCG::LoadShader(const TCHAR *shaderFile) { CCGShader cgShader; TCHAR shaderPath[MAX_PATH]; TCHAR tempPath[MAX_PATH]; CGprofile vertexProfile, fragmentProfile; GLenum error; if(!fboFunctionsLoaded) { MessageBox(NULL, TEXT("Your OpenGL graphics driver does not support framebuffer objects.\nYou will not be able to use CG shaders in OpenGL mode."), TEXT("CG Error"), MB_OK|MB_ICONEXCLAMATION); return false; } vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); cgGLDisableProfile(vertexProfile); cgGLDisableProfile(fragmentProfile); ClearPasses(); if(prevTex) { glDeleteTextures(1,&prevTex); prevTex=0; } if (shaderFile == NULL || *shaderFile==TEXT('\0')) return true; lstrcpy(shaderPath,shaderFile); for(int i=lstrlen(shaderPath); i>=0; i--){ if(IS_SLASH(shaderPath[i])){ shaderPath[i]=TEXT('\0'); break; } } SetCurrentDirectory(shaderPath); if(!cgShader.LoadShader(_tToChar(shaderFile))) return false; cgGLSetOptimalOptions(vertexProfile); cgGLSetOptimalOptions(fragmentProfile); /* insert dummy pass that will contain the original texture */ shaderPasses.push_back(shaderPass()); for(CCGShader::passVector::iterator it=cgShader.shaderPasses.begin(); it!=cgShader.shaderPasses.end();it++) { shaderPass pass; pass.scaleParams = it->scaleParams; /* if this is the last pass (the only one that can have CG_SCALE_NONE) and no filter has been set use the GUI setting */ if(pass.scaleParams.scaleTypeX==CG_SCALE_NONE && !it->filterSet) { pass.linearFilter = GUI.BilinearFilter; } else { pass.linearFilter = it->linearFilter; } // paths in the meta file can be relative _tfullpath(tempPath,_tFromChar(it->cgShaderFile),MAX_PATH); char *fileContents = ReadShaderFileContents(tempPath); if(!fileContents) return false; pass.cgVertexProgram = cgCreateProgram( cgContext, CG_SOURCE, fileContents, vertexProfile, "main_vertex", NULL); checkForCgError("Compiling vertex program"); pass.cgFragmentProgram = cgCreateProgram( cgContext, CG_SOURCE, fileContents, fragmentProfile, "main_fragment", NULL); checkForCgError("Compiling fragment program"); delete [] fileContents; if(!pass.cgVertexProgram || !pass.cgFragmentProgram) { return false; } cgGLLoadProgram(pass.cgVertexProgram); cgGLLoadProgram(pass.cgFragmentProgram); /* generate framebuffer and texture for this pass and apply default texture settings */ glGenFramebuffers(1,&pass.fbo); glGenTextures(1,&pass.tex); glBindTexture(GL_TEXTURE_2D,pass.tex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); shaderPasses.push_back(pass); } for(std::vector::iterator it=cgShader.lookupTextures.begin();it!=cgShader.lookupTextures.end();it++) { lookupTexture tex; strcpy(tex.id,it->id); /* generate texture for the lut and apply specified filter setting */ glGenTextures(1,&tex.tex); glBindTexture(GL_TEXTURE_2D,tex.tex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, it->linearfilter?GL_LINEAR:GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, it->linearfilter?GL_LINEAR:GL_NEAREST); _tfullpath(tempPath,_tFromChar(it->texturePath),MAX_PATH); // simple file extension png/tga decision int strLen = strlen(it->texturePath); if(strLen>4) { if(!strcasecmp(&it->texturePath[strLen-4],".png")) { int width, height; bool hasAlpha; GLubyte *texData; if(loadPngImage(tempPath,width,height,hasAlpha,&texData)) { glPixelStorei(GL_UNPACK_ROW_LENGTH, width); glTexImage2D(GL_TEXTURE_2D, 0, hasAlpha ? 4 : 3, width, height, 0, hasAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, texData); free(texData); } } else if(!strcasecmp(&it->texturePath[strLen-4],".tga")) { STGA stga; if(loadTGA(tempPath,stga)) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stga.width); glTexImage2D(GL_TEXTURE_2D, 0, 4, stga.width, stga.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, stga.data); } } } lookupTextures.push_back(tex); } /* enable texture unit 1 for the lookup textures */ glClientActiveTexture(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2,GL_FLOAT,0,lut_coords); glClientActiveTexture(GL_TEXTURE0); /* generate a texture that we can swap with the original texture and pass back to the main opengl code */ glGenTextures(1,&prevTex); glBindTexture(GL_TEXTURE_2D,prevTex); glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,512,512,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,NULL); glBindTexture(GL_TEXTURE_2D,0); prevTexSize.x = prevTexSize.y = prevTexImageSize.x = prevTexImageSize.y = 0; memset(prevTexCoords,0,sizeof(prevTexCoords)); shaderLoaded = true; return true; } void CGLCG::setTexCoords(int pass,xySize inputSize,xySize textureSize,bool topdown) { float tX = inputSize.x / textureSize.x; float tY = inputSize.y / textureSize.y; // last pass uses top-down coordinates, all others bottom-up if(topdown) { shaderPasses[pass].texcoords[0] = 0.0f; shaderPasses[pass].texcoords[1] = tY; shaderPasses[pass].texcoords[2] = tX; shaderPasses[pass].texcoords[3] = tY; shaderPasses[pass].texcoords[4] = tX; shaderPasses[pass].texcoords[5] = 0.0f; shaderPasses[pass].texcoords[6] = 0.0f; shaderPasses[pass].texcoords[7] = 0.0f; } else { shaderPasses[pass].texcoords[0] = 0.0f; shaderPasses[pass].texcoords[1] = 0.0f; shaderPasses[pass].texcoords[2] = tX; shaderPasses[pass].texcoords[3] = 0.0f; shaderPasses[pass].texcoords[4] = tX; shaderPasses[pass].texcoords[5] = tY; shaderPasses[pass].texcoords[6] = 0.0f; shaderPasses[pass].texcoords[7] = tY; } glTexCoordPointer(2, GL_FLOAT, 0, shaderPasses[pass].texcoords); } void CGLCG::Render(GLuint &origTex, xySize textureSize, xySize inputSize, xySize viewportSize, xySize windowSize) { GLenum error; frameCnt++; CGprofile vertexProfile, fragmentProfile; if(!shaderLoaded) return; vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); cgGLEnableProfile(vertexProfile); cgGLEnableProfile(fragmentProfile); /* set up our dummy pass for easier loop code */ shaderPasses[0].tex = origTex; shaderPasses[0].outputSize = inputSize; shaderPasses[0].textureSize = textureSize; /* loop through all real passes */ for(int i=1;i2) { for(int i=1;iwidth; outHeight = info_ptr->height; switch (info_ptr->color_type) { case PNG_COLOR_TYPE_RGBA: outHasAlpha = true; break; case PNG_COLOR_TYPE_RGB: outHasAlpha = false; break; default: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); return false; } unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr); *outData = (unsigned char*) malloc(row_bytes * outHeight); png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); for (int i = 0; i < outHeight; i++) { memcpy(*outData+(row_bytes * i), row_pointers[i], row_bytes); } /* Clean up after the read, * and free any memory allocated */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); /* Close the file */ fclose(fp); /* That's it */ return true; } #else bool CGLCG::loadPngImage(const TCHAR *name, int &outWidth, int &outHeight, bool &outHasAlpha, GLubyte **outData) { /* No PNG support */ return false; } #endif bool CGLCG::loadTGA(const TCHAR *filename, STGA& tgaFile) { FILE *file; unsigned char type[4]; unsigned char info[6]; file = _tfopen(filename, TEXT("rb")); if (!file) return false; fread (&type, sizeof (char), 3, file); fseek (file, 12, SEEK_SET); fread (&info, sizeof (char), 6, file); //image type either 2 (color) or 3 (greyscale) if (type[1] != 0 || (type[2] != 2 && type[2] != 3)) { fclose(file); return false; } tgaFile.width = info[0] + info[1] * 256; tgaFile.height = info[2] + info[3] * 256; tgaFile.byteCount = info[4] / 8; if (tgaFile.byteCount != 3 && tgaFile.byteCount != 4) { fclose(file); return false; } long imageSize = tgaFile.width * tgaFile.height * tgaFile.byteCount; //allocate memory for image data unsigned char *tempBuf = new unsigned char[imageSize]; tgaFile.data = new unsigned char[tgaFile.width * tgaFile.height * 4]; //read in image data fread(tempBuf, sizeof(unsigned char), imageSize, file); //swap line order and convert to RBGA for(int i=0;i