comparison R/sdl.rnw @ 0:0cfcc391201f

initial from svn dyncall-1745
author Daniel Adler
date Thu, 19 Mar 2015 22:26:28 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:0cfcc391201f
1 % \VignetteIndexEntry{Multimedia programming with R, rdyncall, libSDL and OpenGL}
2 % \VignetteKeyword{low-level}
3 % \VignetteKeyword{rdyncall}
4 % \VignetteKeyword{dyncall}
5 % \VignetteKeyword{multimedia}
6 \documentclass{article}
7 \usepackage{url}
8 \begin{document}
9 \title{Multimedia programming with R, rdyncall, libSDL and OpenGL}
10 \author{Daniel Adler}
11 \maketitle
12 \section{Introduction}
13
14 Via the rdyncall package it is possible to write system-level
15 software in R. In this vignette we focus on multimedia programming
16 in R using the SDL and OpenGL shared libraries.
17 This vignette describes several issues involved in using the rdyncall
18 package with C level interfaces of OpenGL and SDL. This text is
19 not at all a reference to OpenGL or SDL. There are other much better
20 text books and tutorials available.
21
22 See \url{http://www.libsdl.org} and \url{http://www.opengl.org} for further
23 links to tutorials.
24
25 \section{Dynamic R bindings of shared libraries}
26
27 Initially, one needs to dynamically link SDL, OpenGL and OpenGL Utility
28 Library to the R session.
29 This can be done using the rdyncall package and the dynports.
30
31 <<>>=
32 library(rdyncall)
33 dynport(SDL)
34 dynport(GL)
35 dynport(GLU)
36 @
37
38 \section{Initialize SDL}
39
40 We need to initialize the SDL library using the \verb@SDL_Init()@ function
41 giving a bitmask of subsystems to be enabled (such as Video, Audio, Joystick etc.).
42
43 <<>>=
44 subsystems <- SDL_INIT_VIDEO
45 SDL_Init(subsystems)
46 @
47
48 The variable subsystem is a bitmask encoded in a double constant, also
49 the C API expects to have an unsigned int. rdyncall cares for a correct
50 coercion to unsigned int. By addition of several double constants,
51 we can initialize multiple subsystems.
52
53 To see what subsystems SDL offers, we can explore the SDL dynport using e.g.
54 the apropos base R command:
55
56 <<>>=
57 apropos("SDL_INIT_")
58 @
59
60 \section{Opening an SDL/OpenGL Video Surface}
61
62 Let us open a new OpenGL Display using \verb@SDL_SetVideoMode()@. The parameters are:
63
64 \begin{description}
65 \item[width] The width in pixel.
66 \item[height] The height in pixel
67 \item[bpp]
68 \item[flags] Bit flags to be combined with '+'. \verb@SDL_OPENGL@ and \verb@SDL_DOUBLEBUF@ are typical for OpenGL real-time applications.
69 \end{description}
70
71 Some useful Flags:
72
73 \begin{description}
74 \item[SDL\_OPENGL] Request an OpenGL context to be initialized for this surfacce.
75 \item[SDL\_DOUBLEBUF] Rendering into a back buffer and allow to flip the surfaces in one shot.
76 \item[SDL\_FULLSCREEN] Put the display in fullscreen mode.
77 \end{description}
78
79 <<eval=FALSE>>=
80 width <- 640
81 height <- 480
82 bitsperpixel <- 32
83 flags <- SDL_OPENGL+SDL_DOUBLEBUF
84 surface <- SDL_SetVideoMode(width,height,bitsperpixel,flags)
85 @
86
87 The surface object can be printed in R:
88
89 <<eval=FALSE>>=
90 print(surface)
91 @
92
93 \section{Clear the screen and update}
94
95 To clear the display with a constant color value, one needs to specifying the
96 \emph{clear color} first and then clear the \emph{color buffer}.
97
98 <<eval=FALSE>>=
99 glClearColor(0.1,0.2,0.3,0.0)
100 glClear(GL_COLOR_BUFFER_BIT)
101 @
102
103 To see the results, we need to flip the surface - all operations in
104 \emph{double buffering} graphics mode are done in the back and will
105 be presented in one shot to prevent semi-finished drawing artefacts.
106
107 <<eval=FALSE>>=
108 SDL_GL_SwapBuffers()
109 @
110
111 \section{Writing the mainloop}
112
113 This was interactive, but SDL and OpenGL are designed for writing
114 multimedia applications and we do this in R.
115
116 These types of applications run a simple loop such as the interactive
117 loop of R (read-evaluate-print loop).
118 The loop will run until the application should quit. A reference play time
119 will be computed for each loop iteration using \verb@SDL_GetTicks()@ (divided by 1000
120 gives seconds).
121 We will clear our display using a blinking color which changes intensity from
122 black to a specified color. We drop the loop after 3 seconds.
123
124 <<eval=FALSE>>=
125 blinkspeed <- 2.0
126
127 draw <- function(t) {
128 intensity <- t %% blinkspeed / blinkspeed
129 glClearColor(0.8*intensity,0.6*intensity,1.0*intensity,0.0)
130 glClear(GL_COLOR_BUFFER_BIT)
131 }
132
133 mainloop <- function()
134 {
135 quit <- FALSE
136
137 starttime <- SDL_GetTicks() / 1000
138
139 playtime <- 0
140 while(!quit) {
141 # blink the screen using an intensity blending from 0 to 1
142
143 draw(playtime)
144
145 SDL_GL_SwapBuffers()
146
147 # update playtime:
148 now <- SDL_GetTicks() / 1000.0
149 playtime <- now - starttime
150
151 # stop after three seconds
152 if (playtime > 3.0) quit <- TRUE
153 }
154 }
155 # run the loop
156 mainloop()
157 @
158
159 \section{Rendering 3D graphics}
160
161 To render a 3D scene, one specifies the 3D projection first, sets up the
162 camera position in space and then specifies the positions and primitive
163 geometries such as points, lines and triangles.
164
165 First we specify the attributes of our virtual camera, that is
166 the field of view angle, the aspect ratio between width and height (which
167 should match the one of our surface) and the near and far z-plane orthogonal
168 to the camera center projection vector.
169
170 <<eval=FALSE>>=
171 setupProjection <- function()
172 {
173 glMatrixMode(GL_PROJECTION)
174 glLoadIdentity()
175 fovy <- 60.0
176 aspect <- width / height
177 znear <- 10
178 zfar <- 100
179 gluPerspective(fovy, aspect, znear, zfar)
180 }
181 @
182
183 Next, we need to setup the position of our camera in space using
184 the gluLookAt API which gets three vectors eye, center and up.
185
186 <<eval=FALSE>>=
187 setupCamera <- function(eye=c(0,0,-2),center=c(0,0,0),up=c(0,1,0))
188 {
189 glMatrixMode(GL_MODELVIEW)
190 glLoadIdentity()
191 gluLookAt(eye[[1]],eye[[2]],eye[[3]],center[[1]],center[[2]],center[[3]],up[[1]],up[[2]],up[[3]])
192 }
193 @
194
195 What follows is a short routine to draw a million 3D coordinates generated
196 by rnorm.
197
198 <<eval=FALSE>>=
199 draw <- function()
200 {
201 npoints <- 1000000
202 dims <- 3
203 data <- rnorm(npoints*dims)
204 type <- GL_DOUBLE
205 stride <- 0
206 glEnableClientState(GL_VERTEX_ARRAY_POINTER)
207 glVertexPointer(dims,type,stride,data)
208 stgrtindex <- 0
209 glDrawArrays(GL_POINTS, startindex, npoints)
210 glDisableClientState(GL_VERTEX_ARRAY_POINTER)
211 }
212 @
213
214 \section{Handling User-Input Events}
215
216 We will exchange the default behaviour of stopping after three seconds
217 with processing user-input events such as mouse, keyboard and joystick input
218 or pressing the close window button.
219
220 SDL uses a C structure to report user-interface events. One calls
221 a function called \verb@SDL_PollEvent()@ given the reference of this structure.
222
223 <<eval=FALSE>>=
224 processEvents <- function(env)
225 {
226 evt <- new.struct(SDL_Event)
227 quit <- FALSE
228 while(!quit) {
229 while( SDL_PollEvent(evt) ) {
230 type <- evt$type
231 if (type == SDL_QUIT) {
232 env$quit <- TRUE
233 } else if (type == SDL_MOUSEMOTION) {
234 motion <- evt$motion
235 cat(motion$xrel,",",motion$yrel,"\n")
236 }
237 }
238 }
239 }
240 @
241
242 \end{document}
243
244