/* Last saved: Sat 10 Jul 2010 12:57:04 PM*/ /* Copyright (c) 1998 Kenneth Albanowski. All rights reserved. * Copyright (c) 2007 Bob Free. All rights reserved. * Copyright (c) 2009 Chris Marshall. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the same terms as Perl itself. */ /* OpenGL GLX bindings */ #define IN_POGL_GLX_XS #include #include "pgopogl.h" #ifdef HAVE_GL #include "gl_util.h" /* Note: this is caching procs once for all contexts */ /* !!! This should instead cache per context */ #if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_W32API)) #define loadProc(proc,name) \ { \ if (!proc) \ { \ proc = (void *)wglGetProcAddress(name); \ if (!proc) croak(name " is not supported by this renderer"); \ } \ } #define testProc(proc,name) ((proc) ? 1 : !!(proc = (void *)wglGetProcAddress(name))) #else /* not using WGL */ #define loadProc(proc,name) #define testProc(proc,name) 1 #endif /* not defined _WIN32, __CYGWIN__, and HAVE_W32API */ #endif /* defined HAVE_GL */ #ifdef HAVE_GLX #include "glx_util.h" #endif /* defined HAVE_GLX */ #ifdef HAVE_GLU #include "glu_util.h" #endif /* defined HAVE_GLU */ #ifdef IN_POGL_GLX_XS #ifdef HAVE_GLX # define HAVE_GLpc /* Perl interface */ # define nativeWindowId(d, w) (w) static Bool WaitForNotify(Display *d, XEvent *e, char *arg) { return (e->type == MapNotify) && (e->xmap.window == (Window)arg); } # define glpResizeWindow(s1,s2,w,d) XResizeWindow(d,w,s1,s2) # define glpMoveWindow(s1,s2,w,d) XMoveWindow(d,w,s1,s2) # define glpMoveResizeWindow(s1,s2,s3,s4,w,d) XMoveResizeWindow(d,w,s1,s2,s3,s4) #endif /* defined HAVE_GLX */ #ifdef __PM__ # define HAVE_GLpc /* Perl interface */ # define auxXWindow() (croak("Not implemented: auxXWindow"),0) HMQ hmq; AV *EventAv; unsigned long LastEventMask; /* !! Common for all the windows */ Display myDisplay; #endif /* defined __PM__ */ static int debug = 0; #ifdef HAVE_GLpc # define NUM_ARG 7 /* Number of mandatory args to glpcOpenWindow */ Display *dpy; int dpy_open; XVisualInfo *vi; Colormap cmap; XSetWindowAttributes swa; Window win; GLXContext ctx; static int default_attributes[] = { GLX_DOUBLEBUFFER, GLX_RGBA, None }; #endif /* defined HAVE_GLpc */ static int DBUFFER_HACK = 0; #define __had_dbuffer_hack() (DBUFFER_HACK) #endif /* End IN_POGL_GLX_XS */ /********************/ /* GPGPU Utils */ /********************/ GLint FBO_MAX = -1; /* Get max GPGPU data size */ int gpgpu_size(void) { #if defined(GL_ARB_texture_rectangle) && defined(GL_ARB_texture_float) && \ defined(GL_ARB_fragment_program) && defined(GL_EXT_framebuffer_object) if (FBO_MAX == -1) { if (testProc(glProgramStringARB,"glProgramStringARB") && testProc(glGenProgramsARB,"glGenProgramsARB") && testProc(glBindProgramARB,"glBindProgramARB") && testProc(glIsProgramARB,"glIsProgramARB") && testProc(glProgramLocalParameter4fvARB,"glProgramLocalParameter4fvARB") && testProc(glDeleteProgramsARB,"glDeleteProgramsARB") && testProc(glGenFramebuffersEXT,"glGenFramebuffersEXT") && testProc(glGenRenderbuffersEXT,"glGenRenderbuffersEXT") && testProc(glBindFramebufferEXT,"glBindFramebufferEXT") && testProc(glFramebufferTexture2DEXT,"glFramebufferTexture2DEXT") && testProc(glBindRenderbufferEXT,"glBindRenderbufferEXT") && testProc(glRenderbufferStorageEXT,"glRenderbufferStorageEXT") && testProc(glFramebufferRenderbufferEXT,"glFramebufferRenderbufferEXT") && testProc(glCheckFramebufferStatusEXT,"glCheckFramebufferStatusEXT") && testProc(glDeleteRenderbuffersEXT,"glDeleteRenderbuffersEXT") && testProc(glDeleteFramebuffersEXT,"glDeleteFramebuffersEXT")) { glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT,&FBO_MAX); } else { FBO_MAX = 0; } } return(FBO_MAX); #else return(0); #endif } /* Get max square array width for a given GPGPU data size */ int gpgpu_width(int len) { int max = gpgpu_size(); if (max && len && !(len%3)) { int count = len / 3; int w = (int)sqrt(count); while ((w <= count) && (w <= max)) { if (!(count%w)) return(w); w++; } } return(0); } #ifdef GL_ARB_fragment_program static char affine_prog[] = "!!ARBfp1.0\n" "PARAM affine[4] = {program.local[0..3]};\n" "TEMP decal;\n" "TEX decal, fragment.texcoord[0], texture[0], RECT;\n" "MOV decal.w, 1.0;\n" "DP4 result.color.x, decal, affine[0];\n" "DP4 result.color.y, decal, affine[1];\n" "DP4 result.color.z, decal, affine[2];\n" "END\n"; /* Enable affine shader program */ void enable_affine(oga_struct * oga) { if (!oga) return; if (!oga->affine_handle) { /* Load shader program */ glGenProgramsARB(1,&oga->affine_handle); glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB,oga->affine_handle); glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(affine_prog),affine_prog); /* Validate shader program */ if (!glIsProgramARB(oga->affine_handle)) { GLint errorPos; glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB,&errorPos); if (errorPos < 0) errorPos = strlen(affine_prog); croak("Affine fragment program error\n%s",&affine_prog[errorPos]); } } glEnable(GL_FRAGMENT_PROGRAM_ARB); } /* Disable affine shader program */ void disable_affine(oga_struct * oga) { if (!oga) return; if (oga->affine_handle) glDisable(GL_FRAGMENT_PROGRAM_ARB); } #endif #ifdef GL_EXT_framebuffer_object /* Unbind an FBO to an OGA */ void release_fbo(oga_struct * oga) { if (oga->fbo_handle) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glDeleteFramebuffersEXT(1,&oga->fbo_handle); } if (oga->tex_handle) { glBindTexture(oga->target,0); if (oga->tex_handle[0]) glDeleteTextures(1,&oga->tex_handle[0]); if (oga->tex_handle[1]) glDeleteTextures(1,&oga->tex_handle[1]); } } /* Enable an FBO bound to an OGA */ void enable_fbo(oga_struct * oga, int w, int h, GLuint target, GLuint pixel_type, GLuint pixel_format, GLuint element_size) { if (!oga) return; if ((oga->fbo_w != w) || (oga->fbo_h != h) || (oga->target != target) || (oga->pixel_type != pixel_type) || (oga->pixel_format != pixel_format) || (oga->element_size != element_size)) release_fbo(oga); if (!oga->fbo_handle) { GLenum status; /* Save params */ oga->fbo_w = w; oga->fbo_h = h; oga->target = target; oga->pixel_type = pixel_type; oga->pixel_format = pixel_format; oga->element_size = element_size; /* Set up FBO */ glGenTextures(2,oga->tex_handle); glGenFramebuffersEXT(1,&oga->fbo_handle); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,oga->fbo_handle); glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,w,0,h); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(target,oga->tex_handle[1]); glTexParameteri(target,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(target,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(target,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(target,GL_TEXTURE_WRAP_T,GL_CLAMP); glTexImage2D(target,0,pixel_type,w,h,0, pixel_format,element_size,0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,target,oga->tex_handle[1],0); status = glCheckFramebufferStatusEXT(GL_RENDERBUFFER_EXT); if (status) croak("enable_fbo status: %04X\n",status); } else { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,oga->fbo_handle); } /* Load data */ glBindTexture(target,oga->tex_handle[0]); glTexImage2D(target,0,pixel_type,w,h,0, pixel_format,element_size,oga->data); glEnable(target); //glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); glBindTexture(target,oga->tex_handle[0]); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); } /* Disable an FBO bound to an OGA */ void disable_fbo(oga_struct * oga) { if (!oga) return; if (oga->fbo_handle) { glDisable(oga->target); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0); } } #endif MODULE = OpenGL::GL::Top PACKAGE = OpenGL #// Test for GL int _have_gl() CODE: #ifdef HAVE_GL RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_GL */ OUTPUT: RETVAL #// Test for GLU int _have_glu() CODE: #ifdef HAVE_GLU RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_GLU */ OUTPUT: RETVAL #// Test for GLUT int _have_glut() CODE: #if defined(HAVE_GLUT) || defined(HAVE_FREEGLUT) RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_GLUT or HAVE_FREEGLUT */ OUTPUT: RETVAL #// Test for FreeGLUT int _have_freeglut() CODE: #if defined(HAVE_FREEGLUT) RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_FREEGLUT */ OUTPUT: RETVAL #// Test for GLX int _have_glx() CODE: #ifdef HAVE_GLX RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_GLX */ OUTPUT: RETVAL #// Test for GLpc int _have_glp() CODE: #ifdef HAVE_GLpc RETVAL = 1; #else RETVAL = 0; #endif /* defined HAVE_GLpc */ OUTPUT: RETVAL # /* 13000 lines snipped */ ##################### GLU ######################### ############################## GLUT ######################### # /* This is assigned to GLX for now. The glp*() functions should be split out */ #ifdef IN_POGL_GLX_XS # /* The following material is directly copied from Stan Melax's original OpenGL-0.4 */ #ifdef __PM__ #// morphPM(); void morphPM() #endif int __had_dbuffer_hack() #ifdef HAVE_GLpc /* GLX or __PM__ */ #// $ID = glpcOpenWindow($x,$y,$w,$h,$pw,$steal,$event_mask,@attribs); HV * glpcOpenWindow(x,y,w,h,pw,event_mask,steal, ...) int x int y int w int h int pw long event_mask int steal CODE: { XEvent event; Window pwin = (Window)pw; unsigned int err; int *attributes = default_attributes + 1; int *a_buf = NULL; RETVAL = newHV(); /* Create hash to return GL Object info */ if(items > NUM_ARG){ int i; a_buf = (int *) malloc((items-NUM_ARG+2) * sizeof(int)); a_buf[0] = GLX_DOUBLEBUFFER; /* Preallocate */ attributes = a_buf + 1; for (i=NUM_ARG; iscreen), vi->visual, AllocNone); /* create a window */ swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = event_mask; #endif /* defined HAVE_GLX */ if (!pwin) { pwin = RootWindow(dpy, vi->screen); if (debug) printf("Using root as parent window 0x%x\n", pwin); } if (steal) { win = nativeWindowId(dpy, pwin); /* What about depth/visual */ } else { win = XCreateWindow(dpy, pwin, x, y, w, h, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); /* NOTE: PDL code had CWBackPixel above */ } if (!win) { croak("No Window"); } else { if (debug) printf("win = 0x%x\n", win); } XMapWindow(dpy, win); #ifndef HAVE_GLX /* For OS/2 GLX emulation stuff -chm 2009.09.14 */ /* On OS/2: cannot create a context before mapping something... */ /* create a GLX context */ ctx = glXCreateContext(dpy, vi, 0, GL_TRUE); if (!ctx) croak("No context!\n"); LastEventMask = event_mask; #else /* HAVE_GLX, this is the default branch */ if ( (event_mask & StructureNotifyMask) && !steal ) { XIfEvent(dpy, &event, WaitForNotify, (char*)win); } #endif /* not defined HAVE_GLX */ /* connect the context to the window */ if (!glXMakeCurrent(dpy, win, ctx)) croak("Non current"); if (debug) printf("Display=0x%x Window=0x%x Context=0x%x\n",dpy, win, ctx); /* Create the GL object hash information */ hv_store(RETVAL, "Display", strlen("Display"), newSViv(PTR2IV(dpy)), 0); hv_store(RETVAL, "Window", strlen("Window"), newSViv( (IV) win ), 0); hv_store(RETVAL, "Context", strlen("Context"), newSViv(PTR2IV(ctx)), 0); hv_store(RETVAL, "GL_Version",strlen("GL_Version"), newSVpv((char *) glGetString(GL_VERSION),0),0); hv_store(RETVAL, "GL_Vendor",strlen("GL_Vendor"), newSVpv((char *) glGetString(GL_VENDOR),0),0); hv_store(RETVAL, "GL_Renderer",strlen("GL_Renderer"), newSVpv((char *) glGetString(GL_RENDERER),0),0); /* clear the buffer */ glClearColor(0,0,0,1); while ( (err = glGetError()) != GL_NO_ERROR ) { printf("ERROR issued in GL %s\n", gluErrorString(err)); } } OUTPUT: RETVAL #// glpRasterFont(name,base,number,d) int glpRasterFont(name,base,number,d) char *name int base int number Display *d CODE: { XFontStruct *fi; int lb; fi = XLoadQueryFont(d,name); if(fi == NULL) { die("No font %s found",name); } lb = glGenLists(number); if(lb == 0) { die("No display lists left for font %s (need %d)",name,number); } glXUseXFont(fi->fid, base, number, lb); RETVAL=lb; } OUTPUT: RETVAL #// glpPrintString(base,str); void glpPrintString(base,str) int base char *str CODE: { glPushAttrib(GL_LIST_BIT); glListBase(base); glCallLists(strlen(str),GL_UNSIGNED_BYTE,(GLubyte*)str); glPopAttrib(); } #// glpDisplay(); Display * glpDisplay(name) char *name CODE: /* get a connection */ if (!dpy_open) { dpy = XOpenDisplay(name); dpy_open = 1; } if (!dpy) croak("No display!"); RETVAL = dpy; OUTPUT: RETVAL #// glpMoveResizeWindow($x, $y, $width, $height[, $winID[, $display]]); void glpMoveResizeWindow(x, y, width, height, w=win, d=dpy) void* d GLXDrawable w int x int y unsigned int width unsigned int height #// glpMoveWindow($x, $y[, $winID[, $display]]); void glpMoveWindow(x, y, w=win, d=dpy) void* d GLXDrawable w int x int y #// glpResizeWindow($width, $height[, $winID[, $display]]) void glpResizeWindow(width, height, w=win, d=dpy) void* d GLXDrawable w unsigned int width unsigned int height # If glpOpenWindow was used then glXSwapBuffers should be called # without parameters (i.e. use the default parameters) #// glXSwapBuffers([$winID[, $display]]) void glXSwapBuffers(w=win,d=dpy) void * d GLXDrawable w CODE: { glXSwapBuffers(d,w); } #// XPending([$display]); int XPending(d=dpy) void * d CODE: { RETVAL = XPending(d); } OUTPUT: RETVAL #// glpXNextEvent([$display]); void glpXNextEvent(d=dpy) void * d PPCODE: { XEvent event; char buf[10]; KeySym ks; XNextEvent(d,&event); switch(event.type) { case ConfigureNotify: EXTEND(sp,3); PUSHs(sv_2mortal(newSViv(event.type))); PUSHs(sv_2mortal(newSViv(event.xconfigure.width))); PUSHs(sv_2mortal(newSViv(event.xconfigure.height))); break; case KeyPress: case KeyRelease: EXTEND(sp,2); PUSHs(sv_2mortal(newSViv(event.type))); XLookupString(&event.xkey,buf,sizeof(buf),&ks,0); buf[0]=(char)ks;buf[1]='\0'; PUSHs(sv_2mortal(newSVpv(buf,1))); break; case ButtonPress: case ButtonRelease: EXTEND(sp,7); PUSHs(sv_2mortal(newSViv(event.type))); PUSHs(sv_2mortal(newSViv(event.xbutton.button))); PUSHs(sv_2mortal(newSViv(event.xbutton.x))); PUSHs(sv_2mortal(newSViv(event.xbutton.y))); PUSHs(sv_2mortal(newSViv(event.xbutton.x_root))); PUSHs(sv_2mortal(newSViv(event.xbutton.y_root))); PUSHs(sv_2mortal(newSViv(event.xbutton.state))); break; case MotionNotify: EXTEND(sp,4); PUSHs(sv_2mortal(newSViv(event.type))); PUSHs(sv_2mortal(newSViv(event.xmotion.state))); PUSHs(sv_2mortal(newSViv(event.xmotion.x))); PUSHs(sv_2mortal(newSViv(event.xmotion.y))); break; case Expose: default: EXTEND(sp,1); PUSHs(sv_2mortal(newSViv(event.type))); break; } } #// glpXQueryPointer([$winID[, $display]]); void glpXQueryPointer(w=win,d=dpy) void * d GLXDrawable w PPCODE: { int x,y,rx,ry; Window r,c; unsigned int m; XQueryPointer(d,w,&r,&c,&rx,&ry,&x,&y,&m); EXTEND(sp,3); PUSHs(sv_2mortal(newSViv(x))); PUSHs(sv_2mortal(newSViv(y))); PUSHs(sv_2mortal(newSViv(m))); } #endif /* defined HAVE_GLpc */ #// glpSetDebug(flag); void glpSetDebug(flag) int flag CODE: { debug = flag; } #//# glpReadTex($file); void glpReadTex(file) char * file CODE: { GLsizei w,h; int d,i; char buf[250]; unsigned char *image; FILE *fp; char *ret; fp=fopen(file,"r"); if(!fp) croak("couldn't open file %s",file); ret = fgets(buf,250,fp); /* P3 */ if (buf[0] != 'P' || buf[1] != '3') croak("Format is not P3 in file %s",file); ret = fgets(buf,250,fp); while (buf[0] == '#' && fgets(buf,250,fp)); /* Empty */ if (2 != sscanf(buf,"%d%d",&w,&h)) croak("couldn't read image size from file %s",file); if (1 != fscanf(fp,"%d",&d)) croak("couldn't read image depth from file %s",file); if(d != 255) croak("image depth != 255 in file %s unsupported",file); if(w>10000 || h>10000) croak("suspicious size w=%d d=%d in file %s", w, d, file); New(1431, image, w*h*3, unsigned char); for(i=0;i