% Clear the workspace clear; close all; % Assumed distance of the screen dist = 100; % Rendered distance of the object objectDist = dist; % Assumed height of the screen screenHeight = 30; % How much to scale the mesh by in X, Y and Z scaleFactor = 15; %-------------------------------------------------------------------------- % Load in the .obj file. Here we use a funciton from the excellent % gptoolbox for Matlab. We have found that the LoadOBJFile included with % Psychtoolbox fails to load many common .obj files. % % If you use this, you will need to set you "addpath" appropriately. % % gptoolbox: https://github.com/alecjacobson/gptoolbox % %-------------------------------------------------------------------------- % Path to the mesh subfolder of the gptoolbox. You will need to alter this % to the path on your own computer addpath('/Users/peterscarfe/Documents/GitHub Local/gptoolbox/mesh') % Load in the object mesh: the downloaded .obj file needs to be in the same % folder as this piece of code. [verticesAndColour, faces, texCoords, triTexCoords, normals, faceNormals] = readOBJ([cd filesep 'latticeObject.obj']); % Split the vertices and vertex colours: the first three are x, y, z 3D % coordinates, the last three the R, G, B colours. vertices = verticesAndColour(:, 1:3); colors = verticesAndColour(:, 4:6); % Number of vertices and faces [numVerts, ~] = size(vertices); [numFaces, ~] = size(faces); %---------------------------------------------------------------------- % Pretty standard PTB setup here %---------------------------------------------------------------------- % Here we call some default settings for setting up Psychtoolbox PsychDefaultSetup(2); % Skip sync tests (for demo purposes only - not to be used in a real % experiment) Screen('Preference', 'SkipSyncTests', 2); % Find the screen to use for display screenid = max(Screen('Screens')); % Initialise OpenGL InitializeMatlabOpenGL; % Number of samples per pixel for multisampling multiSample = 4; % Background colour bkGrColor = 0; % Open the main window with multi-sampling for anti-aliasing [window, windowRect] = PsychImaging('OpenWindow', screenid, bkGrColor, [], [], [], [], multiSample); % Query the frame duration ifi = Screen('GetFlipInterval', window); % Maximum priority level topPriorityLevel = MaxPriority(window); Priority(topPriorityLevel); % For this demo we will assume our screen is 30cm in height. The units are % essentially arbitary with OpenGL as it is all about ratios. But it is % nice to define things in normal scale numbers ar = windowRect(3) / windowRect(4); screenWidth = screenHeight * ar; %---------------------------------------------------------------------- % Keyboard information %---------------------------------------------------------------------- % Unify the keyboard names for mac and windows computers KbName('UnifyKeyNames'); % Define the keyboard keys that are listened for escapeKey = KbName('ESCAPE'); % Signal no key is being pressed down keyIsDown = 0; %---------------------------------------------------------------------- % Open GL Lighting Setup %---------------------------------------------------------------------- % Start the OpenGL context (you have to do this before you issue OpenGL % commands such as we are using here) Screen('BeginOpenGL', window); % Enable lighting glEnable(GL.LIGHTING); % Ambient light: Force there to be none glLightModelfv(GL.LIGHT_MODEL_AMBIENT, [0 0 0 1]); % Defuse white light glEnable(GL.LIGHT1); glLightfv(GL.LIGHT1, GL.DIFFUSE, [1 1 1 1]); % Enable proper occlusion handling via depth tests glEnable(GL.DEPTH_TEST); % Enable Normalisation glEnable(GL.NORMALIZE); % Set point size glPointSize(0.5); % Enable material color glEnable(GL.COLOR_MATERIAL); %---------------------------------------------------------------------- % Vertex Buffer Object (VBO) Setup %---------------------------------------------------------------------- % Make vertices, normals, faces and colors into line vectors. This is % needed for the setup of our buffers vertLine = reshape(vertices', 1, numVerts * 3); normalLine = reshape(normals', 1, numVerts * 3); facesLine = reshape(faces', 1, numFaces * 3); colorLine = reshape(colors', 1, numVerts * 3); % Faces detailed needed for our draw elements call facesLineInt = int32(facesLine - 1); facesBy3 = numFaces * 3; % Number of bytes in a floating point number - this is needed to % appropriately set up the size of our buffers bytesPerNum = 4; % Calculate the buffer size in bytes normSize = length(normalLine) * bytesPerNum; vertSize = length(vertLine) * bytesPerNum; colorSize = length(colorLine) * bytesPerNum; bufferSize = normSize + vertSize + colorSize; % The buffer ID bufferID = glGenBuffers(1); % Bind the buffer glBindBuffer(GL.ARRAY_BUFFER, bufferID); % Allocate but don't initialize it the buffer by providing a null pointer glBufferData(GL.ARRAY_BUFFER, bufferSize, 0, GL.STATIC_DRAW); % Fill the buffer: note the conversion to single i.e. floating point % numbers glBufferSubData(GL.ARRAY_BUFFER, 0, vertSize, single(vertLine)); glBufferSubData(GL.ARRAY_BUFFER, vertSize, normSize, single(normalLine)); glBufferSubData(GL.ARRAY_BUFFER, vertSize + normSize, colorSize, single(colorLine)); % Get the size of the buffer and check if it is correct. If it is not % correct abort theSize = glGetBufferParameteriv(GL.ARRAY_BUFFER, GL.BUFFER_SIZE); if theSize ~= bufferSize glDeleteBuffersARB(1, bufferID); disp('Buffer size incorrect: aborting') sca return end % Unbind the buffer glBindBuffer(GL.ARRAY_BUFFER, 0); %---------------------------------------------------------------------- % Perspective Projection setup %---------------------------------------------------------------------- % Lets set up a projection matrix, the projection matrix defines how images % in our 3D simulated scene are projected to the images on our 2D monitor glMatrixMode(GL.PROJECTION); glLoadIdentity; % Calculate the field of view in the y direction using the assumed distance angle = 2 * atand(screenHeight / dist); % Set up our perspective projection. This is defined by our field of view % (here given by the variable "angle") and the aspect ratio of our frustum % (our screen) and two clipping planes. These define the minimum and % maximum distances allowable here 0.1cm and 200cm. If we draw outside of % these regions then the stimuli won't be rendered gluPerspective(angle, ar, 0.1, 200); % Setup modelview matrix: This defines the position, orientation and % looking direction of the virtual camera that will be look at our scene. glMatrixMode(GL.MODELVIEW); glLoadIdentity; % Location of the camera is at the origin cam = [0 0 0]; % Set our camera to be looking directly down the Z axis (depth) of our % coordinate system fix = [0 0 -dist]; % Define "up": here we say that up is positive Y up = [0 1 0]; % Here we set up the attributes of our camera using the variables we have % defined in the last three lines of code gluLookAt(cam(1), cam(2), cam(3), fix(1), fix(2), fix(3), up(1), up(2), up(3)); % Set background color to 'black' (the 'clear' color) glClearColor(0, 0, 0, 0); % Clear glClear; % End the open GL context for now Screen('EndOpenGL', window); % We will rotate the model over time theRot = rand(1, 3) .* 360; maxRotPerFrame = repmat(0.3, 1, 3); % We will update the animation every frame waitframes = 1; % Get a first flip timetamp vbl = Screen('Flip', window); % Draw our anitmation loop while ~KbCheck(-1) % End the open GL context Screen('BeginOpenGL', window); % Clear stuff for the round of drawing glClear; %------------------------------------ % Draw the scene %------------------------------------ % Push the matrix stack glPushMatrix; % Translate the object in xyz glTranslatef(0, 0, -objectDist); glRotatef(theRot(1), 1, 0, 0) glRotatef(theRot(2), 0, 1, 0) glRotatef(theRot(3), 0, 0, 1) glScalef(scaleFactor, scaleFactor, scaleFactor); %------------------------------------------------------- % VBO stuff: this is the part where we draw the object %------------------------------------------------------- % Bind the buffer glBindBuffer(GL.ARRAY_BUFFER, bufferID); % Enable all of the arrays we need glEnableClientState(GL.VERTEX_ARRAY); glEnableClientState(GL.NORMAL_ARRAY); glEnableClientState(GL.COLOR_ARRAY); % Set up our pointers glColorPointer(3, GL.FLOAT, 0, vertSize + colorSize); glNormalPointer(GL.FLOAT, 0, vertSize); glVertexPointer(3, GL.FLOAT, 0, 0); % Draw our mesh glDrawElements(GL.TRIANGLES, facesBy3, GL.UNSIGNED_INT, facesLineInt); % Disable our arrays glDisableClientState(GL.VERTEX_ARRAY); glDisableClientState(GL.NORMAL_ARRAY); glDisableClientState(GL.COLOR_ARRAY); % Unbind the buffer glBindBuffer(GL.ARRAY_BUFFER, 0); % Pop the matrix stack glPopMatrix; % End the open GL context Screen('EndOpenGL', window); % Flip to the screen vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi); % Increment the rotation in X, Y and Z theRot = theRot + maxRotPerFrame; end % Clean up sca