Author Topic: Example how to use GLUT/OpenGL in OSX (Intel macho32)  (Read 31091 times)

Offline Stino

  • Jr. Member
  • *
  • Posts: 9
Re: Example how to use GLUT/OpenGL in OSX (Intel macho32)
« Reply #15 on: August 10, 2015, 03:22:03 PM »
Just a quick update of this example, since the old example was not longer working with OSX 10.9:

Code: [Select]
;
; Basic OS X calls to GLUT and OpenGL
;
; Example by Stino / ByTeGeiZ
;
; compile with:
; nasm -g -f macho32 bspGLframBuff.asm
; xcrun gcc -framework GLUT -framework OpenGL -m32 -o bspGLframBuff.out bspGLframBuff.o
;

;inclut GLUT/OpenGL stuff
%include "gl.inc"
%include "glut.inc"

;glibc stuff
extern _clock, _malloc, _free, _usleep, _system

;different ways to use OpenGL (all should work)
;%define USE_TIMER_UPDATE 1
;%define USE_DEPTH_BUFFER 1
%define USE_DOUBLE_BUFFER 1
%define USE_VSYNC 1


; inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1

; frame buffer data
%define width         800
%define high          600
%define colordeep       4


; static data
segment .data

; enable/disable vertical retrace sync inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1
enable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 1", 10, 0
disable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 0", 10, 0

window_name: db "OpenGL frame buffer example", 10, 0
window_handle: dd 0
fl_one: dd 1.0
fl_neg_one: dd -1.0
fl_zero: dd 0.0
fl_half: dd 0.5
fl_neg_half: dd -0.5

; variables for random
seed1:   dd  0
seed2:   dd  0

; variables for argc/argv
argc:           dd  0
argv:           dd  0

; for pointer to frame buffer
fbuff:   dd  0

; code
segment .text
 
global _main

; the main function that init OpenGL and install the gl draw function (_display_func)
_main:
        lea     ecx, [esp+4]            ;load adress of argc in stack to ecx
        lea     edx, [esp+8]            ;load adress of argv in stack to edx

        push    ebp ; setup the frame
        mov     ebp, esp

        sub     esp,24      ;add to get rid of 16-bit-alignment-problem (not 28 as normal since we allready push ebp)

%ifdef USE_VSYNC
        mov     [argv],edx
        mov     [argc],ecx
        ;force enable vsync support of GLUT in OSX
        mov     [esp],dword enable_glut_vsync_command
        call    _system
        mov     edx,[argv]
        mov     ecx,[argc]              ;ecx=argc is not longer saved by _system and this will cause problems in _glutInit and so we need to save and restore it now
%endif       

        ;init OpenGL with GLUT
        mov     [esp+4],edx             ;**argv       
        mov     [esp],ecx               ;&argc     
        call    _glutInit               

        ;Init Random numbers
        call    _randomize

        ;get memory for the gl-frame-buffer
        mov     eax,width*high*colordeep
        mov     [esp], eax
        call    _malloc
        ; check if the malloc failed
        test    eax, eax
        jz      _fail_exit
        mov     [fbuff],eax
         
        ;init display mode for OpenGL window
        mov     eax, GLUT_RGB
%ifdef USE_DOUBLE_BUFFER       
        or      eax, GLUT_DOUBLE
%else
        or      eax, GLUT_SINGLE
%endif
%ifdef USE_DEPTH_BUFFER
        or      eax, GLUT_DEPTH
%endif       
        mov     [esp], eax
        call    _glutInitDisplayMode   

        ;define position of OpenGL window
        mov     [esp+4],dword 80               
        mov     [esp],dword 80               
        call    _glutInitWindowPosition

        ;define OpenGL window size
        mov     [esp+4], dword high           
        mov     [esp], dword width           
        call    _glutInitWindowSize   

        ;create OpenGL window
        mov     eax, dword window_name
        mov     [esp], eax
        call    _glutCreateWindow 
        mov     dword [window_handle],eax

%ifdef USE_DEPTH_BUFFER
        ;enable depth buffer
        mov     [esp],dword GL_DEPTH_TEST
        call    _glEnable
%endif       

        ;add own draw function as call back
        mov     [esp], dword _display_func
        call    _glutDisplayFunc 

%ifdef USE_TIMER_UPDATE
        ;add call back that triggers draw update(timer based)
        mov     [esp+8], dword 0
        mov     [esp+4], dword _timer_func
        mov     [esp], dword 1
        call    _glutTimerFunc

%else
        ;add call back that triggers draw update(idle based)
        mov     [esp], dword _idle_func
        call    _glutIdleFunc
%endif

%ifdef USE_VSYNC
        ; disable glut vsync before main loop, since this loop will never terminate normaly
        ; it works if it was enabled while init of glut/OpenGL ;-)
        mov     [esp],dword disable_glut_vsync_command
        call    _system
%endif

        ;start OpenGL main loop
        call    _glutMainLoop

        ; free the malloc'd memory
        mov     eax, dword [fbuff]
        mov     [esp], eax
        call    _free

_pass_exit:
        add     esp,24
        pop     ebp         
        ret
         
_fail_exit:
%ifdef USE_VSYNC
        ;disable glut vsync in case we had an error
        mov     [esp],dword disable_glut_vsync_command
        call    _system
%endif

        mov     eax, 1
        add     esp,24
        pop     ebp
        ret

;##############################################################################

; GL timer function to give GL draw function the command for redraw and also restart the timer
_timer_func:
        sub     esp,28

        ;select OpenGL window
        mov     eax, [window_handle]
        mov     [esp],eax
        call    _glutSetWindow

        ;start redraw of OpenGL
        call    _glutPostRedisplay

        ;restart timer
        mov     [esp+8], dword 0
        mov     [esp+4], dword  _timer_func
        mov     [esp], dword 40 ;the timer
        call    _glutTimerFunc

        add     esp,28
        ret

;##############################################################################

; GL idle function to give GL draw function the command for redraw
_idle_func:
        sub     esp,28

        ;just wait a few micro secs
        mov     [esp], dword 10
        call    _usleep

        ;select OpenGL window
        mov     eax,[window_handle]
        mov     [esp],eax
        call    _glutSetWindow

        ;start redraw of OpenGL
        call    _glutPostRedisplay

        add     esp,28
        ret

;##############################################################################

; the gl draw function (here is the content of OpenGL
_display_func:
        pusha          ; we have to save all registers because newer versions of GLUT framework need a few registers (ebx, edx, esi ...) before and after  call of our _display_func
        sub     esp,28 ;add to get rid of 16-bit-alignment-problem

        ;fill frame buffer with some content
        call    _draw_to_frame_buff

        ;defiene color for clear screen
        mov     eax, dword [fl_one]
        mov     [esp+12],eax
        mov     eax, dword [fl_zero]
        mov     [esp+8],eax
        mov     [esp+4],eax
        mov     [esp],eax
        call    _glClearColor

        ;clear screen
        mov     eax, dword GL_COLOR_BUFFER_BIT
%ifdef USE_DEPTH_BUFFER
        or      eax, dword GL_DEPTH_BUFFER_BIT
%endif       
        mov     [esp],eax
        call    _glClear

        ;set position were _glDrawPixels will start
        mov     eax,dword [fl_neg_one]
        mov     [esp+4],eax
        mov     [esp],eax
        call    _glRasterPos2f
 
        ;draw frame buffer to screen
        mov     eax, dword [fbuff]
        mov     [esp+16],eax
        mov     [esp+12],dword GL_UNSIGNED_BYTE
        mov     [esp+8],dword GL_RGBA
        mov     [esp+4],dword high
        mov     [esp],dword width
        call    _glDrawPixels

%ifdef USE_DOUBLE_BUFFER       
        ;do all actions and than change the buffer
        call    _glutSwapBuffers
%else
        ;so all actions in single buffer mode
        call    _glFlush
%endif

        add     esp,28
        popa
        ret

;##############################################################################

;here draw own content to frame buffer
_draw_to_frame_buff:
        sub     esp,28 ;add to get rid of 16-bit-alignment-problem

        ;get a random number
        mov     [esp],dword 0xFFFFFFFF
        call    _random ;eax has radom number

        ;fill complete frame buffer with a random color
        mov     edi,[fbuff]
        mov     ecx,width*high
        rep     stosd

        add     esp,28
        ret


;##############################################################################

;Init the random number seeds with system time
_randomize:
        ;parameter:
        ;none

        ;return value:
        ;none

        push eax
        push    edx
        xor     eax,eax
        sub     esp,4
        call    _clock  ;nach eax
        add     esp,4
        mov     [seed1],eax
        mov     edx,eax
        sub     esp,4
        call    _clock  ;nach eax
        add     esp,4
        xor     eax,edx
        mul     edx ;eax * edx nach edx:eax
        mov     [seed2],eax
        pop     edx
        pop     eax
        ret

;##############################################################################

;create a new random number
_random:
        ;parameter:
        ;EAX = w, range of random number: 0 - (w-1)

        ;return value:
        ;EAX = new random number

        push    ebx
        push    edx
        mov     ebx,eax
        mov     eax,[seed1]
        mov     edx,[seed2]
        mov     [seed2],eax
        add     eax,edx
        shld    eax,edx,9
        mov     [seed1],eax       
        xor     edx,edx
        div     ebx
        mov     eax,edx
        pop     edx
        pop     ebx
        ret



There were two reasons:
1.)  "call    _system" destroys content of register ecx and so the next "call    _glutInit" that needs this register as input fails with an "Segmentation fault: 11"
2.) Inside the "_display_func" where we draw our content into the GL frame buffer and that is called by GLUT framework, we need to save all registers with an "pusha" and restore them later with an "popa". In newer versions of GLUT framework the registers ebx, edx, esi and edi are used, before and after calling the users _display_func.

Furthermore with newer versions of XCode you should use the following sequence to assemble and to link the frameworks:
Quote
nasm -g -f macho32 bspGLframBuff.asm
xcrun gcc -framework GLUT -framework OpenGL -m32 -o bspGLframBuff.out bspGLframBuff.o
« Last Edit: August 10, 2015, 03:24:11 PM by Stino »