Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Example Code: Selection of Items in OpenGL

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Example Code: Selection of Items in OpenGL

    the following is a powerbasic 7.x opengl example of using the
    "glupickmatrix" function used to select items in an opengl scene.

    please comment to this code at the following forums url: http://www.powerbasic.com/support/pb...ead.php?t=9130

    look forward to hearing from you!

    scott martindale
    [email protected]
    Code:
    '-----------------------------------------------------------------------------------------------
    '-----------------------------------------------------------------------------------------------
    '  "moons.bas" a non-glut, powerbasic 7.x "interpretation"/port of:
    '   richard s. wright jr.'s "moons.c"
    '   opengl superbible, chapter 14
    '   demonstrates opengl hierarchical selection/picking
    '
    '   pb version uses cubes instead of spheres to simplify the code a bit.
    '-----------------------------------------------------------------------------------------------
    'metastatements:       |
    '----------------------'
        #compile exe
        #dim all
    
        #include "win32api.inc"
       '#include "gl.inc"  'manually added gl  functions and macros for this demo
       '#include "glu.inc" 'manually added glu functions and macros for this demo
       
    '-----------------------------------------------------------------------------------------------
    'opengl-subs, functions, and macros (to avoid having to include the full gl.inc for this demo):|
    '----------------------------------------------------------------------------------------------'
        macro glenum     = dword   '32-bit unsigned
        macro glboolean  = byte    '8-bit  unsigned
        macro glbitfield = dword   '32-bit unsigned
        macro glbyte     = byte    '8-bit  signed
        macro glshort    = integer '16-bit signed
        macro glint      = long    '32-bit signed
        macro glubyte    = byte    '8-bit  unsigned
        macro glushort   = word    '16-bit unsigned
        macro gluint     = dword   '32-bit unsigned
        macro glsizei    = long    '32-bit signed
        macro glfloat    = single  'single precision float
        macro glclampf   = single  'single precision float in [0,1]
        macro gldouble   = double  'double precision float
        macro glclampd   = double  'double precision float in [0,1]
        macro glvoid     = any     'no real need, but why not...
        
        macro gl_lighting                         = &h0b50
        macro gl_light0                           = &h4000
        macro gl_light_model_ambient              = &h0b53
        macro gl_ambient_and_diffuse              = &h1602
        macro gl_diffuse                          = &h1201
        macro gl_position                         = &h1203
        macro gl_color_material                   = &h0b57
        macro gl_front                            = &h0404
        macro gl_front_face                       = &h0b46
        macro gl_cull_face                        = &h0b44
        macro gl_depth_test                       = &h0b71
        macro gl_ccw                              = &h0901
        macro gl_modelview                        = &h1700
        macro gl_projection                       = &h1701
        macro gl_quads                            = &h0007
        macro gl_viewport                         = &h0ba2
        macro gl_render                           = &h1c00
        macro gl_select                           = &h1c02
        macro gl_color_buffer_bit                 = &h00004000
        macro gl_depth_buffer_bit                 = &h00000100
        
        declare sub glenable lib "opengl32.dll" alias "glenable" (byval cap as glenum)
        declare sub gllightmodelfv lib "opengl32.dll" alias "gllightmodelfv" (byval pname as glenum,_
                    byref params as glfloat)
        declare sub gllightfv lib "opengl32.dll" alias "gllightfv" (byval light as glenum, _
                    byval pname as glenum, byref params as any)
        declare sub glfrontface lib "opengl32.dll" alias "glfrontface" (byval mode as glenum)
        declare sub glcolormaterial lib "opengl32.dll" alias "glcolormaterial" _
                   (byval face as glenum, byval mode as glenum)
        declare sub glclearcolor lib "opengl32.dll" alias "glclearcolor" (byval red as glclampf, _
                    byval green as glclampf, byval blue as glclampf, byval alpha as glclampf)
        declare sub glviewport lib "opengl32.dll" alias "glviewport" (byval x as glint, _
                    byval y as glint, byval nwidth as glsizei, byval height as glsizei)
        declare sub glmatrixmode lib "opengl32.dll" alias "glmatrixmode" (byval mode as glenum)
        declare sub glloadidentity lib "opengl32.dll" alias "glloadidentity"
        declare sub glfrustum lib "opengl32.dll" alias "glfrustum" (byval nleft as gldouble, _
                    byval nright as gldouble, byval bottom as gldouble, byval top as gldouble, _
                    byval znear as gldouble, byval zfar as gldouble)
        declare sub glclear lib "opengl32.dll" alias "glclear" (byval mask as glbitfield)
        declare sub glmultmatrixf lib "opengl32.dll" alias "glmultmatrixf" (byref m as any)
        declare sub glpushmatrix lib "opengl32.dll" alias "glpushmatrix"
        declare sub glpopmatrix lib "opengl32.dll" alias "glpopmatrix"
        declare sub gltranslatef lib "opengl32.dll" alias "gltranslatef" (byval x as glfloat, _
                    byval y as glfloat, byval z as glfloat)
        declare sub glinitnames lib "opengl32.dll" alias "glinitnames"
        declare sub glpushname lib "opengl32.dll" alias "glpushname" (byval nname as gluint)
        declare sub glpopname lib "opengl32.dll" alias "glpopname"
        declare sub glcolor3ub lib "opengl32.dll" alias "glcolor3ub" (byval red as glubyte, _
                    byval green as glubyte, byval blue as glubyte)
        declare sub glloadname lib "opengl32.dll" alias "glloadname" (byval nname as gluint)
        declare sub glbegin lib "opengl32.dll" alias "glbegin" (byval mode as glenum)
        declare sub glvertex3f lib "opengl32.dll" alias "glvertex3f" (byval x as glfloat, _
                    byval y as glfloat, byval z as glfloat)
        declare sub glend lib "opengl32.dll" alias "glend"
        declare sub glflush lib "opengl32.dll" alias "glflush"
        declare sub glselectbuffer lib "opengl32.dll" alias "glselectbuffer" _
                   (byval size as glsizei, byref buffer as gluint)
        declare sub glgetintegerv lib "opengl32.dll" alias "glgetintegerv" (byval pname as glenum, _
                    byref params as glint)
        declare function glrendermode lib "opengl32.dll" alias "glrendermode" _
                        (byval mode as glenum) as glint
    '-----------------------------------------------------------------------------------------------
    'glu-macros:           |
    '----------------------'
    
    ' gluperspective
        macro gluperspective(fovy, naspect, znear, zfar)
            macrotemp xmin, xmax, ymin, ymax
            dim xmin as gldouble : dim xmax as gldouble
            dim ymin as gldouble : dim ymax as gldouble
            ymax = znear * tan((fovy * 3.14159265358979#)/360#)
            ymin = -ymax
            xmin = ymin * naspect
            xmax = ymax * naspect
            glfrustum xmin, xmax, ymin, ymax, znear, zfar
        end macro
    
    ' glupickmatrix
        macro glupickmatrix(x, y, nwidth, nheight, viewport)
            macrotemp pm, sx, sy, tx, ty
            dim pm(15) as glfloat, sx!, sy!, tx!, ty!
            sx = viewport(2) / nwidth
            sy = viewport(3) / nheight
            tx = (viewport(2) + 2.0 * (viewport(0) - x)) / nwidth
            ty = (viewport(3) + 2.0 * (viewport(1) - y)) / nheight
            pm(0)  = sx : pm(1)  = 0  : pm(2)  = 0 :  pm(3)  = 0
            pm(4)  = 0  : pm(5)  = sy : pm(6)  = 0 :  pm(7)  = 0
            pm(8)  = 0  : pm(9)  = 0  : pm(10) = 1 :  pm(11) = 0
            pm(12) = tx : pm(13) = ty : pm(14) = 0 :  pm(15) = 1
            glmultmatrixf(pm(0))
        end macro
    
    '-----------------------------------------------------------------------------------------------
    'other program-specific macros: |
    '-------------------------------'
        macro buffer_length = 512& 'the length (number of dwords) of the opengl select buffer
        macro earth  = 1???
        macro mars   = 2???
        macro moon1  = 3???
        macro moon2  = 4???
    
       '--------------------------------------------------------------------------------------------
        macro glbox(boxsz)
            glbegin(gl_quads)                                 'draw a quad
                glvertex3f( boxsz, boxsz,-boxsz)              'top right of the quad (top)
                glvertex3f(-boxsz, boxsz,-boxsz)              'top left of the quad (top)
                glvertex3f(-boxsz, boxsz, boxsz)              'bottom left of the quad (top)
                glvertex3f( boxsz, boxsz, boxsz)              'bottom right of the quad (top)
                glvertex3f( boxsz,-boxsz, boxsz)              'top right of the quad (bottom)
                glvertex3f(-boxsz,-boxsz, boxsz)              'top left of the quad (bottom)
                glvertex3f(-boxsz,-boxsz,-boxsz)              'bottom left of the quad (bottom)
                glvertex3f( boxsz,-boxsz,-boxsz)              'bottom right of the quad (bottom)
                glvertex3f( boxsz, boxsz, boxsz)              'top right of the quad (front)
                glvertex3f(-boxsz, boxsz, boxsz)              'top left of the quad (front)
                glvertex3f(-boxsz,-boxsz, boxsz)              'bottom left of the quad (front)
                glvertex3f( boxsz,-boxsz, boxsz)              'bottom right of the quad (front)
                glvertex3f( boxsz,-boxsz,-boxsz)              'top right of the quad (back)
                glvertex3f(-boxsz,-boxsz,-boxsz)              'top left of the quad (back)
                glvertex3f(-boxsz, boxsz,-boxsz)              'bottom left of the quad (back)
                glvertex3f( boxsz, boxsz,-boxsz)              'bottom right of the quad (back)
                glvertex3f(-boxsz, boxsz, boxsz)              'top right of the quad (left)
                glvertex3f(-boxsz, boxsz,-boxsz)              'top left of the quad (left)
                glvertex3f(-boxsz,-boxsz,-boxsz)              'bottom left of the quad (left)
                glvertex3f(-boxsz,-boxsz, boxsz)              'bottom right of the quad (left)
                glvertex3f( boxsz, boxsz,-boxsz)              'top right of the quad (right)
                glvertex3f( boxsz, boxsz, boxsz)              'top left of the quad (right)
                glvertex3f( boxsz,-boxsz, boxsz)              'bottom left of the quad (right)
                glvertex3f( boxsz,-boxsz,-boxsz)              'bottom right of the quad (right)
            glend
        end macro
    
    
       '--------------------------------------------------------------------------------------------
        macro setupgl
           'light values and coordinates
                dim whitelight  as gllightvector
                    whitelight.r = 0.35
                    whitelight.g = 0.35
                    whitelight.b = 0.35
                    whitelight.i = 1.00
                dim sourcelight as gllightvector
                    sourcelight.r = 0.65
                    sourcelight.g = 0.65
                    sourcelight.b = 0.65
                    sourcelight.i = 1.00
                dim lightpos    as gllightvector
                    lightpos.r = 0.00
                    lightpos.g = 0.00
                    lightpos.b = 0.00
                    lightpos.i = 1.00
    
           'enable lighting
                glenable(gl_lighting)
    
           'setup and enable light 0
                gllightmodelfv(gl_light_model_ambient, whitelight.r)
                gllightfv(gl_light0,gl_diffuse, sourcelight)
                gllightfv(gl_light0,gl_position,lightpos)
                glenable (gl_light0)
    
           'enable color tracking, and set polygonal properties
                glenable(gl_color_material)
                glenable(gl_depth_test)    'hidden surface removal
                glfrontface(gl_ccw)        'counter-clockwise polygons face out
                glenable(gl_cull_face)     'do not calculate insides
    
           'set material properties to follow glcolor values
                glcolormaterial(gl_front, gl_ambient_and_diffuse)
    
           'black blue background
                glclearcolor(0.60, 0.60, 0.60, 1.0)
        end macro
    
    '-----------------------------------------------------------------------------------------------
    'forward declarations: |
    '----------------------'
        declare sub renderscene
        declare sub processplanet(byref selectbuff() as gluint)
        declare sub processselection(byval xpos as glint, byval ypos as glint)
        declare sub setuppixelformat(hdc as gluint)
    
    '-----------------------------------------------------------------------------------------------
    'udts:                 |
    '----------------------'
        type gllightvector
            r as glfloat
            g as glfloat
            b as glfloat
            i as glfloat
        end type
    
    '-----------------------------------------------------------------------------------------------
    'winmain function:    |
    '---------------------'
     function winmain (byval hinstance     as gluint, _
                       byval hprevinstance as gluint, _
                       byval lpcmdline     as asciiz ptr, _
                       byval icmdshow      as glint) as glint
    
        local  msg       as tagmsg
        local  wce       as wndclassex
        local  classname as asciiz * 80
        local  hwnd      as gluint
        local  cnt1      as single
        local  cnt2      as single
        local  gltxt     as string
        global drawrect  as rect
        global g_hdc     as gluint
        global aspect    as glfloat
        global glwinwth  as long
        global glwinht   as long
    
        classname         = "powerbasic opengl"
        wce.cbsize        = sizeof(wce)
        wce.style         = %cs_hredraw or %cs_vredraw
        wce.lpfnwndproc   = codeptr(wndproc)
        wce.cbclsextra    = 0
        wce.cbwndextra    = 0
        wce.hinstance     = hinstance
        wce.hicon         = loadicon(hinstance, "opengl pick example")
        wce.hcursor       = loadcursor(%null, byval %idc_arrow)
        wce.hbrbackground = %null
        wce.lpszmenuname  = %null
        wce.lpszclassname = varptr(classname)
        wce.hiconsm       = loadicon(hinstance, byval %idi_application)
    
        if isfalse(registerclassex(wce)) then
            msgbox "unable to register window class."
            exit function
        end if
    
      'create a window using the registered class
            hwnd = createwindowex(%null,_                     ' extended style
                              classname,_                     ' window class name
                              "pick a planet or moon",_       ' window caption
                              %ws_overlappedwindow or _       ' window style
                              %ws_visible or %ws_sysmenu,_
                              100, 100,_                      ' initial x, y position
                              600, 300,_                      ' initial x, y size
                              %null, _                        ' parent window handle
                              %null, _                        ' window menu handle
                              hinstance, _                    ' program instance handle
                              byval %null)                    ' creation parameters
    
            if hwnd = 0 then
                msgbox "unable to create window"
                exit function
            end if
    
      'display the window on the screen, and set window's opengl attributes
            setupgl
            showwindow hwnd, icmdshow
            updatewindow hwnd
            setforegroundwindow hwnd
            setfocus hwnd
    
      'main message loop:
            do
                if peekmessage(msg, hwnd, 0, 0, %pm_noremove) then
                    if getmessage(msg, hwnd, 0, 0) then
                        translatemessage msg
                        dispatchmessage  msg
                        else
                            exit do
                    end if
    
                    else
                        renderscene
                        waitmessage 'keeps program from hogging unused processor time.
                end if
            loop
    
        function = msg.wparam
    end function
    
    
    '-----------------------------------------------------------------------------------------------
    'wndproc function:    |
    '---------------------'
     function wndproc (byval hwnd as gluint,  byval wmsg as gluint, _
                       byval wparam as glint, byval lparam as glint)as glint
    
        static hdc      as gluint
        static hrc      as gluint
    
        select case wmsg
    
            case %wm_activate
                if hiwrd(wparam) then
                    function = 0
                    exit function
                end if
    
            case %wm_create
                hdc   = getdc(hwnd)
                g_hdc = hdc
                call setuppixelformat(hdc)
                hrc = wglcreatecontext(hdc)
                wglmakecurrent hdc, hrc
                call getwindowrect hwnd, drawrect
                function = 0
                exit function
    
            case %wm_close
                wglmakecurrent hdc, %null
                wgldeletecontext hdc
                postquitmessage 0
                function = 0
                exit function
    
            case %wm_char
                select case wparam
                    case %vk_escape
                        wglmakecurrent hdc, %null
                        wgldeletecontext hdc
                        postquitmessage 0
                        function = 0
                        exit function
                end select
    
            case %wm_lbuttondown
                processselection(clng(lowrd(lparam)),clng(hiwrd(lparam)-glwinht))
                function = 0
                exit function
    
           'case %wm_keyup
    
            case %wm_size
               'reset the viewport to new dimensions
                if hiwrd(lparam) < 1 then lparam = (lparam or &h00010000)
                glwinwth = clng(lowrd(lparam))
                glwinht  = clng(hiwrd(lparam))
                glviewport 0, 0, glwinwth, glwinht
                glmatrixmode gl_projection
                glloadidentity
                call getwindowrect hwnd, drawrect
                aspect = glwinwth/glwinht
                gluperspective(45.0!, aspect, 1.0, 425.0)
                glmatrixmode gl_modelview
                glloadidentity
                function = 0
                exit function
    
            case else
                function = defwindowproc(hwnd, wmsg, wparam, lparam)
    
        end select
    
    end function
    
    '-----------------------------------------------------------------------------------------------
    'called to draw scene
        sub renderscene
           'clear the window with current clearing color
                glclear(gl_color_buffer_bit or gl_depth_buffer_bit)
    
           'save the matrix state and do the rotations
                glmatrixmode(gl_modelview)
                glpushmatrix
    
           'translate the whole scene out and into view
                gltranslatef(0.0, 0.0, -300.0)
    
           'initialize the names stack
                glinitnames
                glpushname(0)
    
           'draw the earth
                glpushmatrix
                glcolor3ub(0,0,255)
                gltranslatef(-100.0,0.0,0.0)
                glloadname(earth)
                glbox(30)
    
           'draw the moon
                gltranslatef(45.0, 0.0, 0.0)
                glcolor3ub(220,220,220)
                glpushname(moon1)
                glbox(5)
                glpopname
                glpopmatrix
    
           'draw mars
                glpushmatrix
                glcolor3ub(255,0,0)
                gltranslatef(100.0, 0.0, 0.0)
                glloadname(mars)
                glbox(20)
    
           'draw moon1
                gltranslatef(-40.0, 40.0, 0.0)
                glcolor3ub(220,220,220)
                glpushname(moon1)
                glbox(5)
                glpopname
    
           'draw moon2
                gltranslatef(0.0, -80.0, 0.0)
                glpushname(moon2)
                glbox(5)
                glpopname
                glpopmatrix
    
           'restore the matrix state
                glpopmatrix           'modelview matrix
           'swap display buffers
                glflush               'force opengl to finish-up
                swapbuffers g_hdc     'display updated screen
        end sub
    
    '-----------------------------------------------------------------------------------------------
    'parse the selection buffer to see which planet/moon was selected
        sub processplanet(byref selectbuff() as gluint)
            local id&, count???, cmessage$
    
           'how many names on the name stack
                count = selectbuff(0)
    
           'bottom of the name stack
                id = selectbuff(3)
    
           'select on earth or mars, whichever was picked
                select case(id)
    
                    case earth:
                        cmessage = "you clicked earth."
                       'if there is another name on the name stack,
                       'then it must be the moon that was selected
                       'this is what was actually clicked on.
                        if count = 2 then
                            cmessage = cmessage + $cr + "specifically the moon."
                        end if
                        exit select
    
                    case mars:
                        cmessage = "you clicked mars."
                       'we know the name stack is only two deep. the precise
                       'moon that was selected will be here.
                        if count = 2 then
                            if selectbuff(4) = moon1 then
                                cmessage = cmessage + $cr + _
                               "specifically moon #1."
                            else
                                cmessage = cmessage + $cr + _
                               "specifically moon #2.
                            end if
                        end if
                        exit select
    
                    case else:
                       'if nothing was clicked we shouldn't be here!
                        cmessage = "error - nothing was clicked on!"
    
                end select
    
           'display the message about planet and moon selection
                msgbox " & cmessage
        end sub
    
    '-----------------------------------------------------------------------------------------------
        sub processselection(byval xpos as glint, byval ypos as glint)
           'process the selection, which is triggered by a left mouse click at (xpos, ypos).
    
           'space for selection buffer
                dim selectbuff(buffer_length) as global gluint
    
           'hit counter
                dim hits as glint
    
           'viewport storage
                dim viewport(4) as glint
    
           'setup selection buffer
           reset selectbuff()
                glselectbuffer(buffer_length, selectbuff(0))
    
           'get the viewport
                glgetintegerv(gl_viewport, viewport(0))
                
           'switch to projection and save the matrix
                glmatrixmode(gl_projection)
                glpushmatrix
    
           'change render mode. opengl will only fill the selection buffer in gl_select mode.
                glrendermode(gl_select)
    
           'establish new clipping volume to be unit cube around mouse cursor point (xpos, ypos)
           'and extending two pixels in the vertical and horzontal direction. remember opengl
           'specifies the y coordinate from the bottom, windows from the top. so windows position
           '(as measured from the top) subtract the height and you get it in terms opengl likes.
                glloadidentity
                glupickmatrix(xpos, viewport(3) - ypos, 2,2, viewport)
    
           'apply perspective matrix
                gluperspective(45.0, aspect, 1.0, 425.0)
    
           'draw the scene
                renderscene
    
           'collect the hits. opengl will only make the selection buffer available if in
           'gl_render mode. so, we "call" the glrendermode(gl_render) and set the return value
           '(if any) to a local variable "hits".
                hits = glrendermode(gl_render)
    
           'if there was a hit occured, display the info.
                if hits > 0 then
                    call processplanet(selectbuff())
                end if
    
           'restore the projection matrix
                glmatrixmode(gl_projection)
                glpopmatrix
    
           'go back to modelview for normal rendering
                glmatrixmode(gl_modelview)
    
        end sub
    '-----------------------------------------------------------------------------------------------
      sub setuppixelformat(hdc as gluint)
          local  npixelformat  as glint
    
          static pfd as pixelformatdescriptor
               pfd.nsize           = sizeof(pixelformatdescriptor) 'size of udt structure
               pfd.nversion        = 1                             'version. always set to 1.
               pfd.dwflags         = %pfd_draw_to_window or _      'support window
                                     %pfd_support_opengl or _      'support opengl
                                     %pfd_doublebuffer             'support double buffering
               pfd.ipixeltype      = %pfd_type_rgba                'red, green, blue, & alpha mode
               pfd.ccolorbits      = 32                            '32-bit color mode
               pfd.credbits        = %null                         'ignore color and shift bits...
               pfd.credshift       = %null                         '...
               pfd.cgreenbits      = %null                         '...
               pfd.cgreenshift     = %null                         '...
               pfd.cbluebits       = %null                         '...
               pfd.cblueshift      = %null                         '...
               pfd.calphabits      = %null                         'no alpha buffer
               pfd.calphashift     = %null                         'ignore shift bit.
               pfd.caccumbits      = %null                         'no accumulation buffer
               pfd.caccumredbits   = %null                         'ignore accumulation bits...
               pfd.caccumgreenbits = %null                         '...
               pfd.caccumbluebits  = %null                         '...
               pfd.caccumalphabits = %null                         '... good cereal!    
               pfd.cdepthbits      = 16                            '16-bit z-buffer depth
               pfd.cstencilbits    = %null                         'no stencil buffer
               pfd.cauxbuffers     = %null                         'no auxiliary buffer
               pfd.ilayertype      = %pfd_main_plane               'main drawing plane
               pfd.breserved       = %null                         'reserved
               pfd.dwlayermask     = %null                         'ignore layer masks...
               pfd.dwvisiblemask   = %null                         '...
               pfd.dwdamagemask    = %null                         '...
    
       'choose best matching pixel format, return index.
            npixelformat = choosepixelformat(hdc, pfd)
    
       'set pixel format to device context.
            call setpixelformat(hdc, npixelformat, pfd)
    end sub
    '-----------------------------------------------------------------------------------------------
    '-----------------------------------------------------------------------------------------------


    [this message has been edited by scott j. martindale (edited september 24, 2003).]
    Scott Martindale
    [email protected]
Working...
X