% BitsLutStereoDemo
%
% demonstration of displaying a precalculated texture based movie, whilst
% separately animating the LUT, and whilst
% controlling the FE1 goggles using a Tlock packet.
%
% For use with the OSX version of Psychtoolbox.
% tested with version 1.0.5, Matlab release 14 sp 2 and OSX 10.3.9
% uses the hidden 'LoadNormalizedGammaTable' screen function.
%
% Please note that this demo needs the Bits++ from Cambridge Research Systems!!
%
% Sections have been lifted from the OSX movie demo etc.
%
% 26/04/2005    ejw     wrote it.

clear; close all

% Define screen
whichScreen=max(Screen('Screens'));

% Find the color values which correspond to white and black.  Though on OS
% X we currently only support true color and thus, for scalar color
% arguments,
% black is always 0 and white 255, this rule is not true on other platforms will
% not remain true on OS X after we add other color depth modes.  
white=WhiteIndex(whichScreen);
black=BlackIndex(whichScreen);
gray=GrayIndex(whichScreen);

% Open up the a window on the screen.
% This is done by calling the Screen function of the Psychophysics
% Toolbox. We need to open the onscreen window first, so that
% the offscreen storage allocated subsequently is properly
% matched to the onscreen frame buffer.
[window,screenRect] = Screen('OpenWindow',whichScreen,128,[],32,2);

% THE FOLLOWING STEP IS IMPORTANT.
% make sure the graphics card LUT is set to a linear ramp
% (else the encoded data will not be recognised by Bits++).
% There is a bug with the underlying OpenGL function, hence the scaling 0 to 255/256.  
% This demo will not work using a default gamma table in the graphics card,
% or even if you set the gamma to 1.0, due to this bug.
% This is NOT a bug with Psychtoolbox!
Screen('LoadNormalizedGammaTable',window,linspace(0,(255/256),256)'*ones(1,3));

% draw a gray background on front and back buffers
%Screen('FillRect',window, gray);
%Screen('Flip', window);
%Screen('FillRect',window, gray);

% =================================================================
% CODE NEEDED HERE !
% "linear_lut" should be replaced here with one giving the inverse
% characteristic of the monitor.
% =================================================================
% restore the Bits++ LUT to a linear ramp
linear_lut =  repmat(round(linspace(0, 2^16 -1, 256))', 1, 3);
BitsPlusSetClut(window,linear_lut);

% find out how big the window is
[screenWidth, screenHeight]=Screen('WindowSize', window);

[y,x] = meshgrid(-(screenHeight - 1)/2: (screenHeight - 1)/2, -(screenWidth - 1)/2: (screenWidth - 1)/2);

blend_image = sqrt((exp(-abs(5*x/((screenWidth - 1)/2))).^2) + (exp(-abs(5*y/((screenHeight - 1)/2))).^2));

% encode the image in mono++ format
encoded_image = BitsPlusPackMonoImage(blend_image*((2^16)-1));

Screen('PutImage', window, encoded_image);
Screen(window,'Flip');

Screen('PutImage', window, encoded_image);

% textures can't seem to be bigger than the screen, though they have to be
% square. So the max texture size is the smaller of the screen dimensions!!
max_texture_size = min([screenWidth screenHeight]);

% pre calculate two counter rotating gratings

% some graphics cards have a hardware texture size limit of 256 square
% (any bigger and it is slow). Be aware that textures use up RAM very quickly.
texture_size=180;   % the diagonal dimension of 180 x 180 is 255, which is our
                    % maximum number of LUT entries

if (max_texture_size < texture_size)
    error('screen must have a minimum resolution of 256');
end 

% check that the screen width is at least 524 pixels
if (screenWidth < 524)
    error('window is not big enough to encode the Bits++ CLUT');
end 

image=zeros(texture_size,texture_size,3);
[x,y] = meshgrid(-(texture_size - 1)/2: (texture_size - 1)/2, -(texture_size - 1)/2: (texture_size - 1)/2);

% number of cycles of sine wave in patch
speed=10/359;
cycles_across=5;
 
% unfortunately we have to iterate this, to generate the textures??
for orient=0:359
    fprintf('Calculating angle %d\n',orient);
    
    phase = round(128 + sin(pi*orient/180)*y + cos(pi*orient/180)*x);
    
    image(:,:,1) = 0;
    image(:,:,2) = 0;
    image(:,:,3) = phase;     
  
    left(orient + 1)= Screen('MakeTexture',whichScreen,image); 

    phase = round(128 - sin(pi*orient/180)*y + cos(pi*orient/180)*x);
    
    image(:,:,1) = 0;
    image(:,:,2) = 0;
    image(:,:,3) = phase; 
    
    right(orient + 1)= Screen('MakeTexture',whichScreen,image); 
end 

% create some drifting sinewave LUTs
[index,offset,gun] = meshgrid(0: 255, 0: 359, 1: 3);

% =================================================================
% CODE NEEDED HERE !
% the code needs to apply the inverse characteristic of the monitor.
% =================================================================

luts = (2^16 - 1) * (sin(cycles_across * 2 * pi * (index/255 + (offset*speed))) + 1) / 2;

% now quickly display the previously calculated textures,
% addind the control for the goggles

fprintf('Displaying counter-rotating gratings, hold a key to exit \n');


while(~KbCheck)
    for orient= 1:360
        % encode the LUT
        newClutRow = BitsPlusEncodeClutRow(squeeze(luts(orient,:,:)));

        rect=[0 0 size(newClutRow,2) 1];
        Screen('PutImage', window, newClutRow, rect, rect);       
        
        Screen('DrawTexture',window,left(orient));
        % bitsgoggles adds the one line image to control the digital o/p
        % then calls 'Flip'
        % Note that bitsGoggles controls the goggle state for the frame
        % >after< it is displayed, hence we set the right goggle open now
        bitsGoggles(1,0,window);
        
        % encode the LUT
        newClutRow = BitsPlusEncodeClutRow(squeeze(luts(orient,:,:)));

        rect=[0 0 size(newClutRow,2) 1];
        Screen('PutImage', window, newClutRow, rect, rect);        
    
        Screen('DrawTexture',window,right(orient));
        % bitsgoggles adds the one line image to control the digital o/p
        % then calls 'Flip'
        % Note that bitsGoggles controls the goggle state for the frame
        % >after< it is displayed, hence we set the left goggle open now        
        bitsGoggles(0,1,window);    
    end
end

% reset the digital output data
Mask=0;
Command=0;
Data=zeros(1,248);
bitsEncodeDIO(Mask,Data,Command, window);

% if the system only has one screen, set the LUT in Bits++ to a linear ramp
% if the system has two or more screens, then blank the screen.
if (whichScreen == 0)
    % =================================================================
    % CODE NEEDED HERE !
    % "linear_lut" should be replaced here with one giving the inverse
    % characteristic of the monitor.
    % =================================================================
    % restore the Bits++ LUT to a linear ramp
    linear_lut =  repmat(round(linspace(0, 2^16 -1, 256))', 1, 3);
    BitsPlusSetClut(window,linear_lut);
    
    % draw a gray background on front and back buffers to clear out any old LUTs
    Screen('FillRect',window, gray);
    Screen('Flip', window);
    Screen('FillRect',window, gray);
    Screen('Flip', window);

    % Close the window.
    Screen('CloseAll');    
else
    % Blank the screen
    BitsPlusSetClut(window,zeros(256,3));

    % draw a black background on front and back buffers to clear out any old LUTs
    Screen('FillRect',window, black);
    Screen('Flip', window);
    Screen('FillRect',window, black);
    Screen('Flip', window);

    Screen('CloseAll');
end



