From: Peter Buchlovsky
Subject: OpenGL and CAPI
Date: 
Message-ID: <87bsnzng9x.fsf@cs.bham.ac.uk>
I am using CAPI:OPENGL-PANE to display some polygons in a window. The
problem I've encountered is when I want to redisplay the pane
independently of any user input. I need to do this since between
redisplaying the pane I rotate the polygons. Ideally, there would be a
way of telling CAPI to call some function a given number of times per
second but I couldn't find it in the docs. So far the only way I found
of achieving this effect is by having the "rotate and redisplay loop"
running in a seperate thread like this.

(defun opengl-demo ()
  (let ((interface (capi:display (make-instance 'opengl-interface))))
    (loop until *quit*
     do
     (mp:process-allow-scheduling)
     (progn
       (setq *angle-of-triangle* (+ *angle-of-triangle* 0.8)
             *angle-of-quad* (- *angle-of-quad* 0.6))
       (redisplay-canvas (canvas interface)))))

[ In the above code when #'capi:display is called on an instance of
'opengl-interface I believe it creates another thread. Right? ]

When I call the function above everything works but once I close the
window the listener just hangs. It would be nice if I could get this
fixed but I still think the solution lies elsewhere. I shouldn't need
two seperate loops (one inside CAPI running in another thread) to do
this.

Perhaps I shouldn't be using CAPI at all. I don't have any experience
with either CAPI or OpenGL so I'd greatly appreciate any suggestions
you might have. I've enclosed all the code below.

-- 
Peter

PS. I am using LispWorks 4.1.20 Personal Edition under Windows. I
couldn't get the OpenGL bindings to compile on the Linux version.


(defpackage "DEMO"
  (:use "CL" "OPENGL")
  (:export "OPENGL-DEMO"))

(in-package "DEMO")

(defparameter *quit* nil)

(capi:define-interface opengl-interface ()
  ()
  (:panes
   (canvas capi:opengl-pane
           :configuration '(:rgba t :double-buffered t)
           :min-width 500
           :min-height 300
           :display-callback 'redisplay-canvas
           :resize-callback 'resize-canvas
           :destroy-callback '(lambda () (setq *quit* t))
           :reader canvas))
  (:layouts
   (main capi:column-layout '(canvas) :default t))
  (:default-initargs :title "OpenGL Demo"))

(defparameter *angle-of-triangle* 0.0)
(defparameter *angle-of-quad* 0.0)

(defun initialize-canvas ()
  (gl-shade-model *gl-smooth*)
  (gl-clear-color 0.0 0.0 0.0 0.0)

  (gl-clear-depth 1.0)
  ; (gl-enable *gl-depth-test*)
  (gl-depth-func *gl-equal*)

  (gl-hint *gl-perspective-correction-hint* *gl-nicest*))

(defun redisplay-canvas (canvas &rest args)
  (declare (ignore args))
  (rendering-on (canvas)
    (initialize-canvas)
    (gl-clear *gl-color-buffer-bit*)
    (gl-clear *gl-depth-buffer-bit*)
    (gl-load-identity)

    (gl-translatef -1.5 0.0 -6.0) ;
    (gl-rotatef *angle-of-triangle* 0.0 1.0 0.0)

    (gl-begin *gl-triangles*)
    (gl-color3-f   1.0  0.0  0.0)
    (gl-vertex3-f  0.0  1.0  0.0)
    (gl-color3-f   0.0  1.0  0.0)
    (gl-vertex3-f -1.0 -1.0  0.0)
    (gl-color3-f   0.0  0.0  1.0)
    (gl-vertex3-f  1.0 -1.0  0.0)
    (gl-end)

    (gl-load-identity)
    (gl-translatef 1.5 0.0 -6.0)
    (gl-rotatef *angle-of-quad* 1.0 0.0 0.0)

    (gl-color3-f 0.5 0.5 1.0)
    (gl-begin *gl-quads*)
    (gl-vertex3-f -1.0  1.0  0.0)
    (gl-vertex3-f  1.0  1.0  0.0)
    (gl-vertex3-f  1.0 -1.0  0.0)
    (gl-vertex3-f -1.0 -1.0  0.0)
    (gl-end)

    (swap-buffers canvas)))

(defun resize-canvas (canvas x y width height)
  (declare (ignore x y))
  (when #+harlequin-pc-lisp (win32:is-window-visible 
                              (win32:pane-hwnd (capi:representation canvas)))
        #-harlequin-pc-lisp T
        (rendering-on (canvas)
          (gl-viewport 0 0 width height)

          (gl-matrix-mode *gl-projection*)
          (gl-load-identity)
          (glu-perspective 45.0 (coerce (/ width height) 'float) 0.1 100.0)

          (gl-matrix-mode *gl-modelview*)
          (gl-load-identity))
        (redisplay-canvas canvas)))

(defun opengl-demo ()
  (let ((interface (capi:display (make-instance 'opengl-interface))))
    (loop until *quit*
     do
     (mp:process-allow-scheduling)
     (progn
       (setq *angle-of-triangle* (+ *angle-of-triangle* 0.8)
             *angle-of-quad* (- *angle-of-quad* 0.6))
       (redisplay-canvas (canvas interface)))))

From: Clive Tong
Subject: Re: OpenGL and CAPI
Date: 
Message-ID: <u1yov7x42.fsf@scientia.com>
> When I call the function above everything works but once I close the
> window the listener just hangs. It would be nice if I could get this
> fixed but I still think the solution lies elsewhere. I shouldn't need
> two seperate loops (one inside CAPI running in another thread) to do
> this.

To set *quit* , you need to use the :destroy-callback on the interface.

(capi:define-interface opengl-interface ()
  ()
  (:panes
   (canvas capi:opengl-pane
           :configuration '(:rgba t :double-buffered t)
           :min-width 500
           :min-height 300
           :display-callback 'redisplay-canvas
           :resize-callback 'resize-canvas
	           :reader canvas))
  (:layouts
   (main capi:column-layout '(canvas) :default t))
  (:default-initargs :title "OpenGL Demo"
   :destroy-callback #'(lambda (interface)
                         (setq *quit* t))))
 
It's also better to use capi:execute-with-interface to check that the
redisplay code runs on the same thread as the thread handling the
events for the interface.

(defun opengl-demo ()
  ...
  (capi-internals:execute-with-interface 
     interface
      #'(lambda ()
       (redisplay-canvas (canvas interface))))
  ...)

[This function is documented in KB:10015

http://www1.xanalys.com/support/lisp/kbase.ns4/51fe6e1cdfe748a180256639005a2ba9
/764b31e4985bcd498525670400530d66!OpenDocument
]

See mp:schedule-timer for data on timers.
From: Jochen Schmidt
Subject: Re: OpenGL and CAPI
Date: 
Message-ID: <9fqsgu$5ktvh$1@ID-22205.news.dfncis.de>
Peter Buchlovsky wrote:

> (defun opengl-demo ()
>   (let ((interface (capi:display (make-instance 'opengl-interface))))
>     (loop until *quit*
>      do
>      (mp:process-allow-scheduling)
>      (progn
>        (setq *angle-of-triangle* (+ *angle-of-triangle* 0.8)
>              *angle-of-quad* (- *angle-of-quad* 0.6))
>        (redisplay-canvas (canvas interface)))))
> 
> [ In the above code when #'capi:display is called on an instance of
> 'opengl-interface I believe it creates another thread. Right? ]
> 
> When I call the function above everything works but once I close the
> window the listener just hangs. It would be nice if I could get this
> fixed but I still think the solution lies elsewhere. I shouldn't need
> two seperate loops (one inside CAPI running in another thread) to do
> this.

You could use LispWorks timers. See the "MP" package in the 
reference-manual.

> PS. I am using LispWorks 4.1.20 Personal Edition under Windows. I
> couldn't get the OpenGL bindings to compile on the Linux version.

The distributed OpenGL bindings are not "Linux ready".
You have to do the following to get them running:

1) change all harlequin-pc-lisp conditionals to win32 in (only!) the 
    following files:
   - compile.lisp
   - defsys.lisp
   - fns.lisp
   - ufns.lisp
   - examples/icosahedron.lisp
2) The following form in OPENGL::FIND-X11-VISUAL-INFO
    should be changed from
(glx-Choose-Visual display (x-lib:x-screen-number-of-screen screen)
           (sys:in-static-area
                 (make-array (length params) :element-type '(signed-byte 32)
                                            :initial-contents params)))

to

(glx-Choose-Visual display (x-lib:x-screen-number-of-screen screen)
           #+harlequin-pc-lisp
           (fli:allocate-dynamic-foreign-object :type '(:signed :int) 
:initial-contents params)
          #-harlequin-pc-lisp
          (sys:in-static-area
                 (make-array (length params) :element-type '(signed-byte 32)
                                            :initial-contents params)))


3) As the dependencies of the libGL and libGLU libraries are missing, you 
should do the following:

$ ld -shared -o gl.so -L/usr/X11R6/lib -lGLU -lGL

and then change opengl/load.lisp to contain something like

(fli:register-module "/tmp/gl.so")

My thanks to resolving this problems go to the great Xanalys support-team!

Regards,
Jochen
From: Jochen Schmidt
Subject: Re: OpenGL and CAPI
Date: 
Message-ID: <9fqsnb$5ktvh$2@ID-22205.news.dfncis.de>
Jochen Schmidt wrote:

> 2) The following form in OPENGL::FIND-X11-VISUAL-INFO
>     should be changed from
> (glx-Choose-Visual display (x-lib:x-screen-number-of-screen screen)
>            (sys:in-static-area
>                  (make-array (length params) :element-type '(signed-byte
>                  32)
>                                             :initial-contents params)))
> 
> to
> 
> (glx-Choose-Visual display (x-lib:x-screen-number-of-screen screen)
>            #+harlequin-pc-lisp
>            (fli:allocate-dynamic-foreign-object :type '(:signed :int)
> :initial-contents params)
>           #-harlequin-pc-lisp
>           (sys:in-static-area
>                  (make-array (length params) :element-type '(signed-byte
>                  32)
>                                             :initial-contents params)))

btw. you find OPENGL::FIND-X11-VISUAL-INFO in the file xm-lib.lisp

Regards,
Jochen
From: Peter Buchlovsky
Subject: Re: OpenGL and CAPI
Date: 
Message-ID: <87r8wlmi82.fsf@cs.bham.ac.uk>
In case anyone is still interested in this thread, here is the code I
posted a week ago with changes suggested by Clive Tong. Thanks to
Clive for the hints and Jochen who showed me how to get the Xanalys
OpenGL bindings to work under Linux. Does anyone know why Xanalys
omitted the glBindTexture and glGenTextures functions? Without them I
found it impossible to work with multiple textures so I defined them
myself.

(fli:define-foreign-function (gl-bind-texture "glBindTexture" :source)
    ((target glenum) (texture gluint))
  :result-type :void
  :language :ansi-c)

(fli:define-foreign-function (gl-gen-textures "glGenTextures" :source)
    ((n glsizei) (textures (gl-vector :unsigned-32)))
  :result-type :void
  :language :ansi-c)

Is there a reason why I shouldn't do this?


(defpackage "DEMO"
  (:use "CL" "OPENGL")
  (:export "OPENGL-DEMO"))

(in-package "DEMO")

(defmacro with-redisplay ((pane) &body body)
  (let ((gpane (gensym)))
    `(let ((,gpane ,pane))
       ,@body
       (capi:execute-with-interface
        (capi:element-interface ,gpane)
        #'(lambda ()
            (funcall (capi:output-pane-display-callback ,gpane) ,gpane))))))

(capi:define-interface opengl-interface ()
  ((quitp :initform nil :accessor quitp)
   (initialized-p :initform nil :accessor initialized-p)
   (angle-of-triangle :initform 0.0d0 :accessor angle-of-triangle)
   (angle-of-quad     :initform 0.0d0 :accessor angle-of-quad))
  (:panes
   (canvas capi:opengl-pane
           :configuration '(:rgba t :double-buffered t)
           :min-width 500
           :min-height 300
           :display-callback 'redisplay-canvas
           :resize-callback 'resize-canvas
           :reader canvas))
  (:layouts
   (main capi:column-layout '(canvas) :default t))
  (:default-initargs :auto-menus nil :title "OpenGL Demo"
   :destroy-callback #'(lambda (interface) (setf (quitp interface) t))))

(defun initialize-canvas ()
  (gl-shade-model *gl-smooth*)
  (gl-clear-color 0.0 0.0 0.0 0.0)

  (gl-clear-depth 1.0)
  (gl-enable *gl-depth-test*)
  (gl-depth-func *gl-less*)

  (gl-hint *gl-perspective-correction-hint* *gl-nicest*))

(defun redisplay-canvas (canvas &rest args)
  (declare (ignore args))
  (let ((interface (capi:element-interface canvas)))
    (rendering-on (canvas)
      (unless (initialized-p interface)
        (initialize-canvas)
        (setf (initialized-p interface) t))
      (gl-clear *gl-color-buffer-bit*)
      (gl-clear *gl-depth-buffer-bit*)
      (gl-load-identity)

      (gl-translatef -1.5 0.0 -6.0)
      (gl-rotatef (angle-of-triangle interface) 0.0 1.0 0.0)

      (gl-begin *gl-triangles*)
      (gl-color3-f   1.0  0.0  0.0)
      (gl-vertex3-f  0.0  1.0  0.0)
      (gl-color3-f   0.0  1.0  0.0)
      (gl-vertex3-f -1.0 -1.0  0.0)
      (gl-color3-f   0.0  0.0  1.0)
      (gl-vertex3-f  1.0 -1.0  0.0)
      (gl-end)

      (gl-load-identity)
      (gl-translatef 1.5 0.0 -6.0)
      (gl-rotatef (angle-of-quad interface) 1.0 0.0 0.0)

      (gl-color3-f 0.5 0.5 1.0)
      (gl-begin *gl-quads*)
      (gl-vertex3-f -1.0  1.0  0.0)
      (gl-vertex3-f  1.0  1.0  0.0)
      (gl-vertex3-f  1.0 -1.0  0.0)
      (gl-vertex3-f -1.0 -1.0  0.0)
      (gl-end)

      (swap-buffers canvas))))

(defun resize-canvas (canvas x y width height)
  (declare (ignore x y))
  (when #+win32 (win32:is-window-visible
                  (win32:pane-hwnd (capi:representation canvas)))
        #-win32 T
        (rendering-on (canvas)
          (gl-viewport 0 0 width height)

          (gl-matrix-mode *gl-projection*)
          (gl-load-identity)
          (glu-perspective 45.0 (coerce (/ width height) 'float) 0.1 100.0)

          (gl-matrix-mode *gl-modelview*)
          (gl-load-identity))
        (redisplay-canvas canvas)))

(defun opengl-demo ()
  (let ((interface (capi:display (make-instance 'opengl-interface))))
    (loop until (quitp interface) do
          (mp:process-allow-scheduling)
          unless (quitp interface) do
          (with-redisplay ((canvas interface))
            (incf (angle-of-triangle interface) 0.2d0)
            (incf (angle-of-quad interface)     0.15d0)))))

This code was tested under Linux but not Windows.

-- 
Peter