% Clear the workspace
clear;
close all;
sca;

% Here we call some default settings for setting up Psychtoolbox
PsychDefaultSetup(2);

% Setup Psychtoolbox for OpenGL 3D rendering support and initialize the
% mogl OpenGL for Matlab wrapper
InitializeMatlabOpenGL;

% Distance to the sphere
distanceCm = 60;

% Radius of our earth sphere
earthRad = 8;

% Rotation per second
degPerSec = 15;
currentRotAngle = 0;

% Number of samples per pixel for multisampling
multiSample = 4;

% Find the screen to use for display
screenid = max(Screen('Screens'));

% Set the black and white index
black = BlackIndex(screenid);

% Start the PsychImaging Configuration
PsychImaging('PrepareConfiguration');
PsychImaging('AddTask', 'General', 'FloatingPoint32Bit');

% Open an on screen window using PsychImaging to optimise drawing
[window, winRect] = PsychImaging('OpenWindow', screenid, black,...
    [], 32, 2, [], multiSample);

% Set to maximum priority
topPriorityLevel = MaxPriority(window);
Priority(topPriorityLevel);

% Get the width and height of the window in pixels
[screenXpix, screenYpix] = Screen('WindowSize', window);

% Reported dimensions of the screen in cm
[widthMm, heightMm] = Screen('DisplaySize', screenid);
screenWidth = widthMm / 10;
screenHeight = heightMm / 10;

% Measure the vertical refresh rate of the monitor
ifi = Screen('GetFlipInterval', window);

% Fill the screen black
Screen('FillRect', window, black);
Screen('Flip', window);

% Convert the rotation per second into per frame
degPerFrame = degPerSec * ifi;

% We will update our rotation animation every frame
waitframes = 1;

%----------------------------------------------------------------------
%                 Load and make the earth texture
%----------------------------------------------------------------------

% Load the earth texture and make it into a texture. This is a texture
% freely avaliable from NASA at the following link.
% https://www.visibleearth.nasa.gov/...
% images/57735/the-blue-marble-land-surface-ocean-color-sea-ice-and-clouds
myimg = imread([cd '/NASA Earth.jpg']);
mytex = Screen('MakeTexture', window, myimg, [], 1, 0);

% Make into an OpenGL texture
[gltex, gltextarget] = Screen('GetOpenGLTexture', window, mytex);


%----------------------------------------------------------------------
%                       OpenGL Setup
%----------------------------------------------------------------------

% We start the OpenGL context
Screen('BeginOpenGL', window);

% Set background color to 'black'
glClearColor(0, 0, 0, 0);

% Enable depth buffer
glEnable(GL.DEPTH_TEST);


%----------------------------------------------------------------------
%                 We will use perspective projection
%----------------------------------------------------------------------

% Near and far clipping planes (these difine the rendering volume, anything
% outside of these is not rendered)
clipNear = 0.1;
clipFar = 100;

% Angular subtense of the screen
angle = 2 * atand((screenHeight / 2) / distanceCm);

% Aspect ratio of the screen
aspectRatio = screenWidth / screenHeight;

% 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;
gluPerspective(angle, aspectRatio, clipNear, clipFar);

% Setup modelview matrix: This defines the position, orientation and
% direction of the virtual camera that will  look at our scene with
glMatrixMode(GL.MODELVIEW);
glLoadIdentity;

% Location of the camera
cam = [0 0 0];

% Set our camera to be looking directly down the -Z axis (depth) of our
% coordinate system
fix = [0 0 -1];

% Define "up"
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));


%----------------------------------------------------------------------
%               Setup the lighting for the environment
%----------------------------------------------------------------------

% Enable OpenGL Lighting
glEnable(GL.LIGHTING);

% Force there to be no ambient light (OpenGL default is for there to be
% some)
glLightModelfv(GL.LIGHT_MODEL_AMBIENT, [0 0 0 1]);

% Define a local light source
glEnable(GL.LIGHT0);

% Defuse light only
glLightfv(GL.LIGHT0, GL.DIFFUSE, [1 1 1 1]);

% Point the light at the origin (this is where we will place our sphere)
glLightfv(GL.LIGHT0, GL.SPOT_DIRECTION, [0 0 -distanceCm]);

% Allow normalisation
glEnable(GL.NORMALIZE);


%----------------------------------------------------------------------
%          Enable the earth texture and make the earth
%----------------------------------------------------------------------

% Enable the texture
glEnable(gltextarget);

% Bind the texture for use
glBindTexture(gltextarget, gltex);

% Allow the colour of the texture and the lighting to interact
glTexEnvfv(GL.TEXTURE_ENV,GL.TEXTURE_ENV_MODE,GL.MODULATE);

% Cyclic clamping of the texture
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_S, GL.REPEAT);
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_T, GL.REPEAT);

% Filtring for the texture
glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.LINEAR);

% Make a quadratic sphere
mysphere = gluNewQuadric;

% Generate texture coordinate automatically
gluQuadricTexture(mysphere, GL.TRUE);

% We end the OpenGL context for now
Screen('EndOpenGL', window);

%----------------------------------------------------------------------
%                       Earth rotation loop
%----------------------------------------------------------------------

% Flip to sync us to the screen
vbl = Screen('Flip', window);

while ~KbCheck(-1)

    Screen('BeginOpenGL', window);

    % Clear the buffers
    glClear;

    % Push the matrix stack for this transform
    glPushMatrix;

    % Move the earth down the negative z-axis and rotate it
    glTranslatef(0, 0, -distanceCm)
    glRotatef(currentRotAngle, 0, 1, 0);
    glRotatef(-90, 1, 0, 0);

    % Render the sphere
    gluSphere(mysphere, earthRad, 1000, 1000);

    % Discard the current transforms
    glPopMatrix;

    % We are done with OenGL for now in order to flip
    Screen('EndOpenGL', window);

    % Flip to the screen
    vbl  = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);

    % Increment the rotation
    currentRotAngle = currentRotAngle + degPerFrame;

end

% Clear the screen
sca