domingo, 7 de outubro de 2007

Bouncing Rectangle (OpenGL demo)

Adaptei um dos exemplos do livro OpenGL Super Bible de C para Lisp. Adaptei para a biblioteca cl-opengl (em algum post anterior eu explico como instalar a biblioteca). O programa é bem simples, porém bonitinho, ele desenha um quadrado vermelho que fica zanzando pela tela.

A tradução é extremamente linear, basta trocar o prefixo da função pelo pacote apropriado e tirar o sufixo: glColor3f -> gl:color.

O ponto menos óbvio é definir os callbacks, pois é preciso criar uma função em lisp que será invocada pelo C. Por isso a função tem que ser definida utiliando defcallback do cffi. Os calbacks são referenciados com a função calback.

Quando a função bounce é executada ela trava o REPL, mas ela pode ser executada em um thread separada, através da função make-thread.


(sb-thread:make-thread #'bounce)


Uma experiência muito interessante é modificar o programa com ele rodando, seja em uma thread separada ou não. Modifiquem a função timer, por exemplo multiplicando por 2 o incremento da posição (linhas 28 e 29):

(incf x1 (* 2 xstep))
(incf y1 (* 2 ystep))


Após um C-c C-k no Slime o quadrado aumenta de velocidade imediatamente, tai algo que não rola em C ou Java.

Vejam o código em Lisp abaixo:


;; From OpenGL SuperBible, chap 2, pag 62
;;
;; Bouncing rectangle

(defparameter x1 0.0)
(defparameter y1 0.0)
(defparameter rsize 25)
(defparameter xstep 1)
(defparameter ystep 1)
(defparameter window-width 100)
(defparameter window-height 100)


(cffi:defcallback render-scene :void ()
(gl:clear :color-buffer-bit)
(gl:color 1.0 0 0)
(gl:rect x1 y1 (+ x1 rsize) (- y1 rsize))
(glut:swap-buffers))

(cffi:defcallback timer :void ((value :int))
(declare (ignore value))
(when (or (> x1 (- window-width rsize)) (< x1 (- window-width)))
(setf xstep (- xstep)))
(when (or (> y1 window-height) (< y1 (- rsize window-height)))
(setf ystep (- ystep)))

(incf x1 xstep)
(incf y1 ystep)

#|
Check bounds. THis is in case the window is made
smaller while the rectangel is bouncing and the
rectangle suddenly finds itself outside the new
clipping volume
|#
(if (> x1 (+ window-width (- rsize) xstep))
(setf x1 (- window-width rsize 1))
(when (< x1 (- (+ window-width xstep)))
(setf x1 (- (+ window-width 1)))))

(glut:post-redisplay)
(glut:timer-func 33 (cffi:callback timer) 1))


(cffi:defcallback change-size :void ((w :unsigned-int) (h :unsigned-int))

;; Prevent divide by zero
(when (eq h 0)
(setf h 1))

(gl:viewport 0 0 w h)
(gl:matrix-mode :projection)
(gl:load-identity)
(let ((aspect-ratio (/ w h)))
(if (<= w h)
(gl:ortho -100 100 (/ -100 aspect-ratio) (/ 100 aspect-ratio) 1 -1)
(gl:ortho (* -100 aspect-ratio) (* 100 aspect-ratio) -100 100 1 -1))))

(defun setup-rc ()
(gl:clear-color 0.0 0.0 1.0 1.0))

(defun bounce ()

(glut:init "SBCL")
(glut:init-display-mode :double :rgba)
(glut:create-window "Bounce")
(glut:init-window-size 800 600)
(glut:display-func (cffi:callback render-scene))
(glut:reshape-func (cffi:callback change-size))
(glut:timer-func 33 (cffi:callback timer) 1)
(setup-rc)
(glut:main-loop))


E o código original em C esta aqui:


#include < GL/gl.h >
#include < GL/glu.h >
#include < GL/glut.h >
#include < stdio.h >

GLfloat x1 = 0.0f;
GLfloat y1 = 0.0f;
GLfloat rsize=25;

GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;

GLfloat windowWidth=100;
GLfloat windowHeight=100;

void RenderScene() {
glClear(GL_COLOR_BUFFER_BIT);

glColor3f(1.0f,0.0f,0.0f);
glRectf(x1,y1,x1+rsize,y1-rsize);
glutSwapBuffers();
}


void TimerFunction(int value) {

if (x1 > windowWidth-rsize || x1 < -windowWidth) {
xstep = -xstep;
}

if (y1 > windowHeight || y1 < -windowHeight + rsize) {
ystep = -ystep;
}

x1 += xstep;
y1 += ystep;

// Check bounds. Tjos om case tjhe window is made smaller whie
// the rectangle is bouncing and the rectangle suddenly finds
// itself outside the new clipping volume
if (x1 > (windowWidth-rsize + xstep))
x1 = windowWidth-rsize-1;
else
if (x1 < -(windowWidth + xstep))
x1 = -windowWidth - 1;

if(y1 > (windowHeight +ystep))
y1 = windowHeight - 1;
else
if(y1 < -(windowHeight - rsize + ystep))
y1 = -windowHeight + rsize - 1;

glutPostRedisplay();
glutTimerFunc(33,TimerFunction,1);
}

void ChangeSize(GLsizei w,GLsizei h) {
GLfloat aspectRatio;

if (h==0)
h=1;

glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
aspectRatio=(GLfloat)w/(GLfloat)h;

if (w <= h)
glOrtho(-100.0,100.0,-100.0/aspectRatio,100.0/aspectRatio,1.0,-1.0);
else
glOrtho(-100.0*aspectRatio,100.0*aspectRatio,-100.0,100.0,1.0,-1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void SetupRC() {
glClearColor(0.0f,0.0f,1.0f,1.0f);
}

int main(int argc,char *argv[]) {
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutCreateWindow("Bounce");
glutInitWindowSize(800,600);
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutTimerFunc(33,TimerFunction,1);
SetupRC();
glutMainLoop();

return 0;
}


No Linux este programa é compilado com o comando:


gcc bounce.c -o bounce -lGL -lGLU -lglut -lm

Um comentário:

Anônimo disse...
Este comentário foi removido por um administrador do blog.