function SpinningCubeDemo % SpinningCubeDemo - Demonstrate use of MATLAB-OpenGL toolbox % % This demo demonstrates use of OpenGL commands in a Matlab script to % perform some funky 3D animation in Psychtoolbox. % It shows a randomly spinning, textured cube. The six sides of the cube % are textured via binary texture data loaded from a file. % % Stop the demo by pressing any key. % % Notable implementation details: % The call InitializeMatlabOpenGL(1) at the top of the script initializes the % Matlab-OpenGL toolbox and enables the 3D gfx support in Psychtoolbox to % allow proper interfacing between the OpenGL toolbox and Psychtoolbox. % % After this call, all OpenGL functions are made available to Matlab with % the same - or a very similar - calling syntax as in the C programming % language. OpenGL constants are made available in C-Style, e.g., % GL_DEPTH_TEST, and in a format that is optimized for Matlab, where the % first underscore is replaced by a dot, e.g., GL.DEPTH_TEST. The former % style is more convenient if you want to copy & paste OpenGL code written % in C into a Matlab M-File for use, but it only works if you put all your % code into one single M-File or function. The second style works in % subfunctions as well, if you place the commands "global GL" and "global % GLU" at the top of each function... This inconvenience is unavoidable due % to the design of Matlab. % % In order to execute OpenGL 3D drawing commands to draw 3D stims into a % Psychtoolbox Onscreen- or offscreen window, one needs to call % Screen('BeginOpenGL', windowPtr). After OpenGL drawing and before % execution of standard Screen() commands, one needs to call % Screen('EndOpenGL', windowPtr) to tell Psychtoolbox that 3D drawing is % finished. % % Some OpenGL functions that return complex parameters to Matlab are not % yet implemented - this is work in progress. The performance will be also % lower than when coding in a compiled language like C++ or C -- that's the % Matlab tax you'll have to pay ;-) % % Apart from that, use of OpenGL for Matlab is the same as OpenGL for the C % programming language. If you are used to OpenGL coding in C, it should be % a zero effort transition to code in Matlab+PTB. If you don't know OpenGL % then get yourself one of the many good books or visit one of the many % OpenGL tutorials on the internet. % % The OpenGL Red Book is a great introduction and reference for OpenGL % programming. Release 1.0 is available online, later releases can be % purchased in any good book store: % % http://www.opengl.org/documentation/red_book_1.0/ % % For more infos, code samples, tutorials, online documentation, go to: % % http://www.opengl.org % % The OpenGL for Matlab toolbox was developed and contributed under % GPL license by Prof. Richard F. Murray, University of York, Canada. % % 15-Dec-2005 -- created (RFM) % 21-Jan-2006 -- Modified for use with OpenGL-Psychtoolbox (MK) % 16-Feb-2006 -- Modified for use with new MOGL (MK) % 05-Mar-2006 -- Cleaned up for public consumption (MK) % 06-Apr-2013 -- Make compatible with OpenGL-ES1.1. (MK) % Is the script running in OpenGL Psychtoolbox? AssertOpenGL; % Find the screen to use for display: screenid=max(Screen('Screens')); % Setup Psychtoolbox for OpenGL 3D rendering support and initialize the % mogl OpenGL for Matlab wrapper: InitializeMatlabOpenGL(1); % Open a double-buffered full-screen window on the main displays screen. [win , winRect] = Screen('OpenWindow', screenid); % Setup the OpenGL rendering context of the onscreen window for use by % OpenGL wrapper. After this command, all following OpenGL commands will % draw into the onscreen window 'win': Screen('BeginOpenGL', win); % Get the aspect ratio of the screen: ar=winRect(4)/winRect(3); % Turn on OpenGL local lighting model: The lighting model supported by % OpenGL is a local Phong model with Gouraud shading. glEnable(GL_LIGHTING); % Enable the first local light source GL_LIGHT_0. Each OpenGL % implementation is guaranteed to support at least 8 light sources. glEnable(GL_LIGHT0); % Enable two-sided lighting - Back sides of polygons are lit as well. glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); % Enable proper occlusion handling via depth tests: glEnable(GL_DEPTH_TEST); % Define the cubes light reflection properties by setting up reflection % coefficients for ambient, diffuse and specular reflection: glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT, [ .33 .22 .03 1 ]); glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE, [ .78 .57 .11 1 ]); glMaterialfv(GL_FRONT_AND_BACK,GL_SHININESS,27.8); % Enable 2D texture mapping, so the faces of the cube will show some nice % images: glEnable(GL_TEXTURE_2D); % Generate 6 textures and store their handles in vecotr 'texname' texname=glGenTextures(6); % Load a binary file which contains binary pixel data for the six textures: matdemopath = [PsychtoolboxRoot 'PsychDemos/OpenGL4MatlabDemos/mogldemo.mat']; load(matdemopath, 'face') % Setup textures for all six sides of cube: for i=1:6, % Enable i'th texture by binding it: glBindTexture(GL_TEXTURE_2D,texname(i)); % Compute image in matlab matrix 'tx' f=max(min(128*(1+face{i}),255),0); tx=repmat(flipdim(f,1),[ 1 1 3 ]); tx=permute(flipdim(uint8(tx),1),[ 3 2 1 ]); % Assign image in matrix 'tx' to i'th texture: glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,256,256,0,GL_RGB,GL_UNSIGNED_BYTE,tx); % Setup texture wrapping behaviour: glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); % Setup filtering for the textures: glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); % Choose texture application function: It shall modulate the light % reflection properties of the the cubes face: glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); end % Set projection matrix: This defines a perspective projection, % corresponding to the model of a pin-hole camera - which is a good % approximation of the human eye and of standard real world cameras -- % well, the best aproximation one can do with 3 lines of code ;-) glMatrixMode(GL_PROJECTION); glLoadIdentity; % Field of view is 25 degrees from line of sight. Objects closer than % 0.1 distance units or farther away than 100 distance units get clipped % away, aspect ratio is adapted to the monitors aspect ratio: gluPerspective(25,1/ar,0.1,100); % Setup modelview matrix: This defines the position, orientation and % looking direction of the virtual camera: glMatrixMode(GL_MODELVIEW); glLoadIdentity; % Cam is located at 3D position (3,3,5), points upright (0,1,0) and fixates % at the origin (0,0,0) of the worlds coordinate system: gluLookAt(3,3,5,0,0,0,0,1,0); % Setup position and emission properties of the light source: % Set background color to 'black': glClearColor(0,0,0,0); % Point lightsource at (1,2,3)... glLightfv(GL_LIGHT0,GL_POSITION,[ 1 2 3 0 ]); % Emits white (1,1,1,1) diffuse light: glLightfv(GL_LIGHT0,GL_DIFFUSE, [ 1 1 1 1 ]); % There's also some white, but weak (R,G,B) = (0.1, 0.1, 0.1) % ambient light present: glLightfv(GL_LIGHT0,GL_AMBIENT, [ .1 .1 .1 1 ]); % Initialize amount and direction of rotation theta=0; rotatev=[ 0 0 1 ]; % Animation loop: Run until key press... while (1) % Calculate rotation angle for next frame: theta=mod(theta+0.3,360); rotatev=rotatev+0.1*[ sin((pi/180)*theta) sin((pi/180)*2*theta) sin((pi/180)*theta/5) ]; rotatev=rotatev/sqrt(sum(rotatev.^2)); % Setup cubes rotation around axis: glPushMatrix; glRotatef(theta,rotatev(1),rotatev(2),rotatev(3)); % Clear out the backbuffer: This also cleans the depth-buffer for % proper occlusion handling: glClear; % The subroutine cubeface (see below) draws one side of the cube, so we % call it six times with different settings: cubeface([ 4 3 2 1 ],texname(1)); cubeface([ 5 6 7 8 ],texname(2)); cubeface([ 1 2 6 5 ],texname(3)); cubeface([ 3 4 8 7 ],texname(4)); cubeface([ 2 3 7 6 ],texname(5)); cubeface([ 4 1 5 8 ],texname(6)); glPopMatrix; % Finish OpenGL rendering into PTB window and check for OpenGL errors. Screen('EndOpenGL', win); % Show rendered image at next vertical retrace: Screen('Flip', win); % Switch to OpenGL rendering again for drawing of next frame: Screen('BeginOpenGL', win); % Check for keyboard press and exit, if so: if KbCheck break; end; end % Delete all allocated OpenGL textures: glDeleteTextures(length(texname),texname); % Shut down OpenGL rendering: Screen('EndOpenGL', win); % Close onscreen window and release all other ressources: sca; % Well done! return % Subroutine for drawing of one face of a textured cube: % Draw a quadrilateral polygon, defined by indices in vector 'i' and apply % the texture image 'tx' to it: function cubeface( i, tx ) % We want to access OpenGL constants. They are defined in the global % variable GL. GLU constants and AGL constants are also available in the % variables GLU and AGL... global GL % Vector v maps indices to 3D positions of the corners of a face: v=[ 0 0 0 ; 1 0 0 ; 1 1 0 ; 0 1 0 ; 0 0 1 ; 1 0 1 ; 1 1 1 ; 0 1 1 ]'-0.5; % Compute surface normal vector. Needed for proper lighting calculation: n=cross(v(:,i(2))-v(:,i(1)),v(:,i(3))-v(:,i(2))); % Bind (Select) texture 'tx' for drawing: glBindTexture(GL.TEXTURE_2D,tx); % Begin drawing of a new quad: glBegin(GL.QUADS); % Assign n as normal vector for this polygons surface normal: glNormal3f(n(1), n(2), n(3)); % Define vertex 1 by assigning a texture coordinate and a 3D position: glTexCoord2f(0, 0); glVertex3f(v(1,i(1)),v(2,i(1)),v(3,i(1))); % Define vertex 2 by assigning a texture coordinate and a 3D position: glTexCoord2f(1, 0); glVertex3f(v(1,i(2)),v(2,i(2)),v(3,i(2))); % Define vertex 3 by assigning a texture coordinate and a 3D position: glTexCoord2f(1, 1); glVertex3f(v(1,i(3)),v(2,i(3)),v(3,i(3))); % Define vertex 4 by assigning a texture coordinate and a 3D position: glTexCoord2f(0, 1); glVertex3f(v(1,i(4)),v(2,i(4)),v(3,i(4))); % Done with this polygon: glEnd; % Return to main function: return