1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 #include "COpenGL.h"
8 #include "win32_display.h"
9 #include "../snes9x.h"
10 #include "../gfx.h"
11 #include "../display.h"
12 #include "wsnes9x.h"
13 #include <msxml2.h>
14
15 #include "../filter/hq2x.h"
16 #include "../filter/2xsai.h"
17
18
COpenGL(void)19 COpenGL::COpenGL(void)
20 {
21 hDC = NULL;
22 hRC = NULL;
23 hWnd = NULL;
24 drawTexture = 0;
25 initDone = false;
26 afterRenderWidth = 0;
27 afterRenderHeight = 0;
28 outTextureWidth = 0;
29 outTextureHeight = 0;
30 fullscreen = false;
31 shaderFunctionsLoaded = false;
32 shader_type = OGL_SHADER_NONE;
33 pboFunctionsLoaded = false;
34 shaderProgram = 0;
35 vertexShader = 0;
36 fragmentShader = 0;
37 cgContext = NULL;
38 cgVertexProgram = cgFragmentProgram = NULL;
39 cgAvailable = false;
40 frameCount = 0;
41 cgShader = NULL;
42 glslShader = NULL;
43 *currentShaderFile = _T('\0');
44 }
45
~COpenGL(void)46 COpenGL::~COpenGL(void)
47 {
48 DeInitialize();
49 }
50
Initialize(HWND hWnd)51 bool COpenGL::Initialize(HWND hWnd)
52 {
53 int pfdIndex;
54 RECT windowRect;
55
56 this->hWnd = hWnd;
57 this->hDC = GetDC(hWnd);
58
59 PIXELFORMATDESCRIPTOR pfd=
60 {
61 sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
62 1, // Version Number
63 PFD_DRAW_TO_WINDOW | // Format Must Support Window
64 PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
65 PFD_DOUBLEBUFFER, // Must Support Double Buffering
66 PFD_TYPE_RGBA, // Request An RGBA Format
67 16, // Select Our Color Depth
68 0, 0, 0, 0, 0, 0, // Color Bits Ignored
69 0, // No Alpha Buffer
70 0, // Shift Bit Ignored
71 0, // No Accumulation Buffer
72 0, 0, 0, 0, // Accumulation Bits Ignored
73 16, // 16Bit Z-Buffer (Depth Buffer)
74 0, // No Stencil Buffer
75 0, // No Auxiliary Buffer
76 PFD_MAIN_PLANE, // Main Drawing Layer
77 0, // Reserved
78 0, 0, 0 // Layer Masks Ignored
79 };
80 PIXELFORMATDESCRIPTOR pfdSel;
81
82 if(!(pfdIndex=ChoosePixelFormat(hDC,&pfd))) {
83 DeInitialize();
84 return false;
85 }
86 if(!SetPixelFormat(hDC,pfdIndex,&pfd)) {
87 DeInitialize();
88 return false;
89 }
90 if(!(hRC=wglCreateContext(hDC))) {
91 DeInitialize();
92 return false;
93 }
94 if(!wglMakeCurrent(hDC,hRC)) {
95 DeInitialize();
96 return false;
97 }
98
99 ogl_LoadFunctions();
100
101 LoadPBOFunctions();
102
103 wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress( "wglSwapIntervalEXT" );
104
105 glEnableClientState(GL_VERTEX_ARRAY);
106 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
107 glEnable(GL_BLEND);
108 glEnable(GL_TEXTURE_2D);
109
110 glMatrixMode (GL_PROJECTION);
111 glLoadIdentity ();
112 glOrtho (0.0, 1.0, 0.0, 1.0, -1, 1);
113
114 glVertexPointer(2, GL_FLOAT, 0, vertices);
115 glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
116
117 cgAvailable = loadCgFunctions();
118 if(cgAvailable) {
119 cgContext = cgCreateContext();
120 cgShader = new CGLCG(cgContext);
121 }
122
123 if (ShaderAvailable() && NPOTAvailable()) {
124 glslShader = new GLSLShader();
125 }
126
127 ApplyDisplayChanges();
128
129 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
130 glClear(GL_COLOR_BUFFER_BIT);
131 SwapBuffers(hDC);
132
133 initDone = true;
134 return true;
135 }
136
DeInitialize()137 void COpenGL::DeInitialize()
138 {
139 initDone = false;
140 SetShaders(NULL);
141 DestroyDrawSurface();
142 wglMakeCurrent(NULL,NULL);
143 if(hRC) {
144 wglDeleteContext(hRC);
145 hRC = NULL;
146 }
147 if(hDC) {
148 ReleaseDC(hWnd,hDC);
149 hDC = NULL;
150 }
151 hWnd = NULL;
152 afterRenderWidth = 0;
153 afterRenderHeight = 0;
154 outTextureWidth = 0;
155 outTextureHeight = 0;
156 shaderFunctionsLoaded = false;
157 shader_type = OGL_SHADER_NONE;
158 if (glslShader) {
159 delete glslShader;
160 glslShader = NULL;
161 }
162 if(cgShader) {
163 delete cgShader;
164 cgShader = NULL;
165 }
166 if(cgAvailable)
167 unloadCgLibrary();
168 cgAvailable = false;
169 }
170
CreateDrawSurface(unsigned int width,unsigned int height)171 void COpenGL::CreateDrawSurface(unsigned int width, unsigned int height)
172 {
173 HRESULT hr;
174
175 if (!NPOTAvailable()) {
176 unsigned int neededSize = max(width, height);
177 //we need at least 512 pixels (SNES_WIDTH * 2) so we can start with that value
178 unsigned int quadTextureSize = 512;
179 while (quadTextureSize < neededSize)
180 quadTextureSize *= 2;
181 width = height = quadTextureSize;
182 }
183
184 if(!drawTexture) {
185 outTextureWidth = width;
186 outTextureHeight = height;
187 glGenTextures(1,&drawTexture);
188 glBindTexture(GL_TEXTURE_2D,drawTexture);
189 glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA, outTextureWidth, outTextureHeight,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,NULL);
190 if(pboFunctionsLoaded) {
191 glGenBuffers(1,&drawBuffer);
192 glBindBuffer(GL_PIXEL_UNPACK_BUFFER,drawBuffer);
193 glBufferData(GL_PIXEL_UNPACK_BUFFER, outTextureWidth*outTextureHeight *2,NULL,GL_STREAM_DRAW);
194 glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
195 } else {
196 noPboBuffer = new BYTE[outTextureWidth*outTextureHeight *2];
197 }
198
199 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
200 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
201 }
202 }
203
DestroyDrawSurface()204 void COpenGL::DestroyDrawSurface()
205 {
206 if(drawTexture) {
207 glDeleteTextures(1,&drawTexture);
208 drawTexture = NULL;
209 }
210 if(drawBuffer) {
211 glDeleteBuffers(1,&drawBuffer);
212 drawBuffer = NULL;
213 }
214 if(noPboBuffer) {
215 delete [] noPboBuffer;
216 noPboBuffer = NULL;
217 }
218 }
219
ChangeDrawSurfaceSize(unsigned int width,unsigned int height)220 bool COpenGL::ChangeDrawSurfaceSize(unsigned int width, unsigned int height)
221 {
222 DestroyDrawSurface();
223 CreateDrawSurface(width, height);
224 SetupVertices();
225 return true;
226 }
227
SetupVertices()228 void COpenGL::SetupVertices()
229 {
230 vertices[0] = 0.0f;
231 vertices[1] = 0.0f;
232 vertices[2] = 1.0f;
233 vertices[3] = 0.0f;
234 vertices[4] = 1.0f;
235 vertices[5] = 1.0f;
236 vertices[6] = 0.0f;
237 vertices[7] = 1.0f;
238
239 float tX = (float)afterRenderWidth / (float)outTextureWidth;
240 float tY = (float)afterRenderHeight / (float)outTextureHeight;
241
242 texcoords[0] = 0.0f;
243 texcoords[1] = tY;
244 texcoords[2] = tX;
245 texcoords[3] = tY;
246 texcoords[4] = tX;
247 texcoords[5] = 0.0f;
248 texcoords[6] = 0.0f;
249 texcoords[7] = 0.0f;
250 glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
251 }
252
wOGLViewportCallback(int source_width,int source_height,int viewport_x,int viewport_y,int viewport_width,int viewport_height,int * out_dst_x,int * out_dst_y,int * out_dst_width,int * out_dst_height)253 void wOGLViewportCallback(int source_width, int source_height,
254 int viewport_x, int viewport_y,
255 int viewport_width, int viewport_height,
256 int *out_dst_x, int *out_dst_y,
257 int *out_dst_width, int *out_dst_height)
258 {
259 /* get window size here instead of using viewport passed in - we limited the viewport before the glsl render
260 call already, this is simply to position smaller outputs correctly in the actual viewport
261 */
262 RECT windowSize;
263 GetClientRect(GUI.hWnd, &windowSize);
264 RECT displayRect = CalculateDisplayRect(source_width, source_height, windowSize.right, windowSize.bottom);
265 *out_dst_x = displayRect.left;
266 *out_dst_y = displayRect.top;
267 *out_dst_width = displayRect.right - displayRect.left;
268 *out_dst_height = displayRect.bottom - displayRect.top;
269 }
270
Render(SSurface Src)271 void COpenGL::Render(SSurface Src)
272 {
273 SSurface Dst;
274 RECT dstRect;
275 unsigned int newFilterScale;
276 GLenum error;
277
278 if(!initDone) return;
279
280 //create a new draw surface if the filter scale changes
281 dstRect = GetFilterOutputSize(Src);
282 if(outTextureWidth != dstRect.right || outTextureHeight != dstRect.bottom)
283 ChangeDrawSurfaceSize(dstRect.right, dstRect.bottom);
284
285 if(pboFunctionsLoaded) {
286 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, drawBuffer);
287 Dst.Surface = (unsigned char *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
288 } else {
289 Dst.Surface = noPboBuffer;
290 }
291 Dst.Height = outTextureHeight;
292 Dst.Width = outTextureWidth;
293 Dst.Pitch = outTextureWidth * 2;
294
295 RenderMethod (Src, Dst, &dstRect);
296 if(!Settings.AutoDisplayMessages) {
297 WinSetCustomDisplaySurface((void *)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
298 S9xDisplayMessages ((uint16*)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
299 }
300
301 if(pboFunctionsLoaded)
302 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
303
304 if(afterRenderHeight != dstRect.bottom || afterRenderWidth != dstRect.right) {
305 afterRenderHeight = dstRect.bottom;
306 afterRenderWidth = dstRect.right;
307
308 ChangeRenderSize(0,0);
309 }
310
311 glBindTexture(GL_TEXTURE_2D,drawTexture);
312 glPixelStorei(GL_UNPACK_ROW_LENGTH, outTextureWidth);
313 glTexSubImage2D (GL_TEXTURE_2D,0,0,0,dstRect.right-dstRect.left,dstRect.bottom-dstRect.top,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,pboFunctionsLoaded?0:noPboBuffer);
314
315 if(pboFunctionsLoaded)
316 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
317
318 RECT windowSize, displayRect;
319 GetClientRect(hWnd, &windowSize);
320 //Get maximum rect respecting AR setting
321 displayRect = CalculateDisplayRect(afterRenderWidth, afterRenderHeight, windowSize.right, windowSize.bottom);
322
323 // GLSL class does all the rendering, no output needed
324 if (shader_type == OGL_SHADER_GLSL) {
325 glslShader->render(drawTexture, afterRenderWidth, afterRenderHeight, displayRect.left, displayRect.top, displayRect.right - displayRect.left, displayRect.bottom - displayRect.top, wOGLViewportCallback);
326 }
327 else { // for CG shaders and old style .shader files the last pass is done here, same as no shader
328 if(shader_type == OGL_SHADER_CG) {
329 xySize inputSize = { (float)afterRenderWidth, (float)afterRenderHeight };
330 xySize xywindowSize = { (double)windowSize.right, (double)windowSize.bottom };
331 xySize viewportSize = { (double)(displayRect.right - displayRect.left),
332 (double)(displayRect.bottom - displayRect.top) };
333 xySize textureSize = { (double)outTextureWidth, (double)outTextureHeight };
334 cgShader->Render(drawTexture, textureSize, inputSize, viewportSize, xywindowSize);
335 }
336 else if (shader_type == OGL_SHADER_GLSL_OLD) {
337 GLint location;
338
339 float inputSize[2] = { (float)afterRenderWidth, (float)afterRenderHeight };
340 float outputSize[2] = { (float)(GUI.Stretch ? windowSize.right : afterRenderWidth),
341 (float)(GUI.Stretch ? windowSize.bottom : afterRenderHeight) };
342 float textureSize[2] = { (float)outTextureWidth, (float)outTextureHeight };
343 float frameCnt = (float)++frameCount;
344 location = glGetUniformLocation(shaderProgram, "rubyInputSize");
345 glUniform2fv(location, 1, inputSize);
346
347 location = glGetUniformLocation(shaderProgram, "rubyOutputSize");
348 glUniform2fv(location, 1, outputSize);
349
350 location = glGetUniformLocation(shaderProgram, "rubyTextureSize");
351 glUniform2fv(location, 1, textureSize);
352 }
353 if (Settings.BilinearFilter) {
354 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
355 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
356 }
357 else {
358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
359 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
360 }
361
362 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
363 glClear(GL_COLOR_BUFFER_BIT);
364 glDrawArrays(GL_QUADS, 0, 4);
365 }
366
367 glFlush();
368 SwapBuffers(hDC);
369 if (GUI.ReduceInputLag)
370 glFinish();
371 }
372
ChangeRenderSize(unsigned int newWidth,unsigned int newHeight)373 bool COpenGL::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight)
374 {
375 RECT displayRect, windowSize;
376 if(newWidth==0||newHeight==0) {
377 GetClientRect(hWnd,&windowSize);
378 newWidth = windowSize.right;
379 newHeight = windowSize.bottom;
380 }
381 displayRect=CalculateDisplayRect(afterRenderWidth,afterRenderHeight,newWidth,newHeight);
382 glViewport(displayRect.left,newHeight-displayRect.bottom,displayRect.right-displayRect.left,displayRect.bottom-displayRect.top);
383 SetupVertices();
384 return true;
385 }
386
SetSwapInterval(int frames)387 void COpenGL::SetSwapInterval(int frames)
388 {
389 if (wglSwapIntervalEXT)
390 wglSwapIntervalEXT(frames);
391 }
392
ApplyDisplayChanges(void)393 bool COpenGL::ApplyDisplayChanges(void)
394 {
395 if(wglSwapIntervalEXT) {
396 wglSwapIntervalEXT(GUI.Vsync?1:0);
397 }
398 if(GUI.shaderEnabled && GUI.OGLshaderFileName)
399 SetShaders(GUI.OGLshaderFileName);
400 else
401 SetShaders(NULL);
402
403 ChangeRenderSize(0,0);
404 return true;
405 }
406
SetFullscreen(bool fullscreen)407 bool COpenGL::SetFullscreen(bool fullscreen)
408 {
409 if(!initDone)
410 return false;
411
412 if(this->fullscreen==fullscreen)
413 return true;
414
415 this->fullscreen = fullscreen;
416
417 if(fullscreen) {
418 DEVMODE dmScreenSettings={0};
419 dmScreenSettings.dmSize=sizeof(dmScreenSettings);
420 dmScreenSettings.dmPelsWidth = GUI.FullscreenMode.width;
421 dmScreenSettings.dmPelsHeight = GUI.FullscreenMode.height;
422 dmScreenSettings.dmBitsPerPel = GUI.FullscreenMode.depth;
423 dmScreenSettings.dmDisplayFrequency = GUI.FullscreenMode.rate;
424 dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
425 if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) {
426 this->fullscreen = false;
427 return false;
428 }
429 ChangeRenderSize(GUI.FullscreenMode.width,GUI.FullscreenMode.height);
430 } else {
431 ChangeDisplaySettings(NULL,0);
432 }
433
434
435
436 return true;
437 }
438
SetSnes9xColorFormat()439 void COpenGL::SetSnes9xColorFormat()
440 {
441 GUI.ScreenDepth = 16;
442 GUI.BlueShift = 0;
443 GUI.GreenShift = 6;
444 GUI.RedShift = 11;
445 S9xBlit2xSaIFilterInit();
446 S9xBlitHQ2xFilterInit();
447 GUI.NeedDepthConvert = FALSE;
448 GUI.DepthConverted = TRUE;
449 return;
450 }
451
EnumModes(std::vector<dMode> * modeVector)452 void COpenGL::EnumModes(std::vector<dMode> *modeVector)
453 {
454 DISPLAY_DEVICE dd;
455 dd.cb = sizeof(dd);
456 DWORD dev = 0;
457 int iMode = 0;
458 dMode mode;
459
460 while (EnumDisplayDevices(0, dev, &dd, 0))
461 {
462 if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) && (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
463 {
464 DEVMODE dm;
465 memset(&dm, 0, sizeof(dm));
466 dm.dmSize = sizeof(dm);
467 iMode = 0;
468 while(EnumDisplaySettings(dd.DeviceName,iMode,&dm)) {
469 if(dm.dmBitsPerPel>=16) {
470 mode.width = dm.dmPelsWidth;
471 mode.height = dm.dmPelsHeight;
472 mode.rate = dm.dmDisplayFrequency;
473 mode.depth = dm.dmBitsPerPel;
474 modeVector->push_back(mode);
475 }
476 iMode++;
477 }
478 }
479 dev++;
480 }
481 }
482
LoadPBOFunctions()483 bool COpenGL::LoadPBOFunctions()
484 {
485 if(GUI.OGLdisablePBOs)
486 return false;
487
488 if(pboFunctionsLoaded)
489 return true;
490
491 const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
492
493 if(extensions && strstr(extensions, "pixel_buffer_object")) {
494
495 if(glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers && glMapBuffer) {
496 pboFunctionsLoaded = true;
497 }
498
499 }
500 return pboFunctionsLoaded;
501 }
502
LoadShaderFunctions()503 bool COpenGL::LoadShaderFunctions()
504 {
505 if(shaderFunctionsLoaded)
506 return true;
507
508 const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
509
510 if(extensions && strstr(extensions, "fragment_program")) {
511
512 if(glCreateProgram &&
513 glCreateShader &&
514 glCompileShader &&
515 glDeleteShader &&
516 glDeleteProgram &&
517 glAttachShader &&
518 glDetachShader &&
519 glLinkProgram &&
520 glUseProgram &&
521 glShaderSource &&
522 glGetUniformLocation &&
523 glUniform2fv) {
524 shaderFunctionsLoaded = true;
525 }
526 }
527 return shaderFunctionsLoaded;
528 }
529
SetShaders(const TCHAR * file)530 bool COpenGL::SetShaders(const TCHAR *file)
531 {
532 if (file && lstrcmp(file, currentShaderFile) == 0)
533 return true;
534
535 SetShadersCG(NULL);
536 SetShadersGLSL(NULL);
537 SetShadersGLSL_OLD(NULL);
538 shader_type = OGL_SHADER_NONE;
539
540 if (file) {
541 lstrcpy(currentShaderFile, file);
542 if ((lstrlen(file) > 3 && _tcsncicmp(&file[lstrlen(file) - 3], TEXT(".cg"), 3) == 0) ||
543 (lstrlen(file) > 4 && _tcsncicmp(&file[lstrlen(file) - 4], TEXT(".cgp"), 4) == 0)) {
544 return SetShadersCG(file);
545 }
546 else if ((lstrlen(file) > 7 && _tcsncicmp(&file[lstrlen(file) - 7], TEXT(".shader"), 7) == 0)) {
547 return SetShadersGLSL_OLD(file);
548 }
549 else {
550 return SetShadersGLSL(file);
551 }
552 }
553
554 *currentShaderFile = _T('\0');
555
556 return true;
557 }
558
checkForCgError(const char * situation)559 void COpenGL::checkForCgError(const char *situation)
560 {
561 char buffer[4096];
562 CGerror error = cgGetError();
563 const char *string = cgGetErrorString(error);
564
565 if (error != CG_NO_ERROR) {
566 sprintf(buffer,
567 "Situation: %s\n"
568 "Error: %s\n\n"
569 "Cg compiler output...\n", situation, string);
570 MessageBoxA(0, buffer,
571 "Cg error", MB_OK|MB_ICONEXCLAMATION);
572 if (error == CG_COMPILER_ERROR) {
573 MessageBoxA(0, cgGetLastListing(cgContext),
574 "Cg compilation error", MB_OK|MB_ICONEXCLAMATION);
575 }
576 }
577 }
578
SetShadersCG(const TCHAR * file)579 bool COpenGL::SetShadersCG(const TCHAR *file)
580 {
581 if(!cgAvailable) {
582 if(file)
583 MessageBox(NULL, TEXT("The CG runtime is unavailable, CG shaders will not run.\nConsult the snes9x readme for information on how to obtain the runtime."), TEXT("CG Error"),
584 MB_OK|MB_ICONEXCLAMATION);
585 return false;
586 }
587
588 if(!cgShader->LoadShader(file))
589 return false;
590
591 shader_type = OGL_SHADER_CG;
592
593 return true;
594 }
595
SetShadersGLSL(const TCHAR * glslFileName)596 bool COpenGL::SetShadersGLSL(const TCHAR *glslFileName)
597 {
598 if (!glslShader)
599 return false;
600
601 glslShader->destroy();
602
603 if (!glslFileName)
604 return false;
605
606 if(!glslShader->load_shader(_tToChar(glslFileName))) {
607 return false;
608 }
609
610 shader_type = OGL_SHADER_GLSL;
611
612 return true;
613 }
614
SetShadersGLSL_OLD(const TCHAR * glslFileName)615 bool COpenGL::SetShadersGLSL_OLD(const TCHAR *glslFileName)
616 {
617 char *fragment = NULL, *vertex = NULL;
618 IXMLDOMDocument * pXMLDoc = NULL;
619 IXMLDOMElement * pXDE = NULL;
620 IXMLDOMNode * pXDN = NULL;
621 HRESULT hr;
622 BSTR queryString, nodeContent;
623
624 TCHAR errorMsg[MAX_PATH + 50];
625
626 if (fragmentShader) {
627 glDetachShader(shaderProgram, fragmentShader);
628 glDeleteShader(fragmentShader);
629 fragmentShader = 0;
630 }
631 if (vertexShader) {
632 glDetachShader(shaderProgram, vertexShader);
633 glDeleteShader(vertexShader);
634 vertexShader = 0;
635 }
636 if (shaderProgram) {
637 glUseProgram(0);
638 glDeleteProgram(shaderProgram);
639 shaderProgram = 0;
640 }
641
642 if (glslFileName == NULL || *glslFileName == TEXT('\0'))
643 return true;
644
645 if (!LoadShaderFunctions()) {
646 MessageBox(NULL, TEXT("Unable to load OpenGL shader functions"), TEXT("Shader Loading Error"),
647 MB_OK | MB_ICONEXCLAMATION);
648 return false;
649 }
650
651 hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDoc));
652
653 if (FAILED(hr)) {
654 MessageBox(NULL, TEXT("Error creating XML Parser"), TEXT("Shader Loading Error"),
655 MB_OK | MB_ICONEXCLAMATION);
656 return false;
657 }
658
659 VARIANT fileName;
660 VARIANT_BOOL ret;
661 fileName.vt = VT_BSTR;
662 #ifdef UNICODE
663 fileName.bstrVal = SysAllocString(glslFileName);
664 #else
665 wchar_t tempfilename[MAX_PATH];
666 MultiByteToWideChar(CP_UTF8, 0, glslFileName, -1, tempfilename, MAX_PATH);
667 fileName.bstrVal = SysAllocString(tempfilename);
668 #endif
669 hr = pXMLDoc->load(fileName, &ret);
670 SysFreeString(fileName.bstrVal);
671
672 if (FAILED(hr) || hr == S_FALSE) {
673 _stprintf(errorMsg, TEXT("Error loading GLSL shader file:\n%s"), glslFileName);
674 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
675 pXMLDoc->Release();
676 return false;
677 }
678
679 VARIANT attributeValue;
680 BSTR attributeName;
681
682 hr = pXMLDoc->get_documentElement(&pXDE);
683 if (FAILED(hr) || hr == S_FALSE) {
684 _stprintf(errorMsg, TEXT("Error loading root element from file:\n%s"), glslFileName);
685 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
686 pXMLDoc->Release();
687 return false;
688 }
689
690 attributeName = SysAllocString(L"language");
691 pXDE->getAttribute(attributeName, &attributeValue);
692 SysFreeString(attributeName);
693 pXDE->Release();
694
695 if (attributeValue.vt != VT_BSTR || lstrcmpiW(attributeValue.bstrVal, L"glsl")) {
696 _stprintf(errorMsg, TEXT("Shader language is <%s>, expected <GLSL> in file:\n%s"), attributeValue.bstrVal, glslFileName);
697 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
698 if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
699 pXMLDoc->Release();
700 return false;
701 }
702 if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
703
704 queryString = SysAllocString(L"/shader/fragment");
705 hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
706 SysFreeString(queryString);
707
708 if (hr == S_OK) {
709 hr = pXDN->get_text(&nodeContent);
710 if (hr == S_OK) {
711 int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, fragment, 0, NULL, NULL);
712 fragment = new char[requiredChars];
713 WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, fragment, requiredChars, NULL, NULL);
714 }
715 SysFreeString(nodeContent);
716 pXDN->Release();
717 pXDN = NULL;
718 }
719
720 queryString = SysAllocString(L"/shader/vertex");
721 hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
722 SysFreeString(queryString);
723
724 if (hr == S_OK) {
725 hr = pXDN->get_text(&nodeContent);
726 if (hr == S_OK) {
727 int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, vertex, 0, NULL, NULL);
728 vertex = new char[requiredChars];
729 WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, vertex, requiredChars, NULL, NULL);
730 }
731 SysFreeString(nodeContent);
732 pXDN->Release();
733 pXDN = NULL;
734 }
735
736 pXMLDoc->Release();
737
738 if (!fragment && !vertex) {
739 _stprintf(errorMsg, TEXT("No vertex or fragment program in file:\n%s"), glslFileName);
740 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
741 return false;
742 }
743
744 shaderProgram = glCreateProgram();
745 if (vertex) {
746 vertexShader = glCreateShader(GL_VERTEX_SHADER);
747 glShaderSource(vertexShader, 1, (const GLchar **)&vertex, NULL);
748 glCompileShader(vertexShader);
749 glAttachShader(shaderProgram, vertexShader);
750 delete[] vertex;
751 }
752 if (fragment) {
753 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
754 glShaderSource(fragmentShader, 1, (const GLchar **)&fragment, NULL);
755 glCompileShader(fragmentShader);
756 glAttachShader(shaderProgram, fragmentShader);
757 delete[] fragment;
758 }
759
760 glLinkProgram(shaderProgram);
761 glUseProgram(shaderProgram);
762
763 shader_type = OGL_SHADER_GLSL_OLD;
764
765 return true;
766 }
767
ShaderAvailable()768 bool COpenGL::ShaderAvailable()
769 {
770 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
771
772 if (!extensions)
773 return false;
774
775 if (strstr(extensions, "fragment_program") ||
776 strstr(extensions, "fragment_shader"))
777 {
778 return true;
779 }
780
781 return false;
782 }
783
NPOTAvailable()784 bool COpenGL::NPOTAvailable()
785 {
786 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
787 const char *version = (const char *)glGetString(GL_VERSION);
788
789 if (!extensions)
790 return false;
791
792 int glVersionMajor = 0;
793 glVersionMajor = atoi (version);
794
795 if (glVersionMajor >= 2)
796 return true;
797
798 if (strstr(extensions, "non_power_of_two") ||
799 strstr(extensions, "npot"))
800 {
801 return true;
802 }
803
804 return false;
805 }
806