Code covered by the BSD License  

Highlights from
Wolfenstein 3D (Tech Demo)

  • Wolfenstein 3D Tech Demo A playable Wolfenstein 3D demo featuring a real-time discrete ray casting engine. Possibly the coolest MATLAB game demo ever.
  • wolf3d WOLF3D - The main file for Wolfenstein 3D for MATLAB
  • View all files
image thumbnail
from Wolfenstein 3D (Tech Demo) by Mingjing Zhang
A playable Wolfenstein 3D demo featuring real-time ray casting. My most bestest creation by far.

wolf3d
function wolf3d
% WOLF3D - The main file for Wolfenstein 3D for MATLAB
% Usage:
% ------
%  wolf3d;

% History
% -------
% Date              Updater             Modification
% ----              -------             ------------
% Jun 14, 2013      M. Zhang        v0.35: Correct texture projection
% Jun 15, 2013      M. Zhang        v0.40: Correct rendering implemented
% Jun 15, 2013      M. Zhang        v0.45: Attempted Collision Detection
%                                          Added 'Enter' - mouse on/off
%                                           'Escape' - quit
% Jun 20, 2013      M. Zhang        v0.50: Now supports arbitrary resolution
%                                          Added mouse wheel support
% ----              -------             ------------
% Copyright (C) Stellari Studio, 2013
% Mingjing Zhang @ Vision & Media Lab, Simon Fraser University, Canada

%% Variable Declaration
try
    WolfVer = '0.50';     % Last updated Jun 20, 2013
    MainAxesSize = [];    % The size of the main axes, same as GAME_RESOLUTION
    FPS = [];             % Frames-per-second, ideally over 60
    
    FRAME_DURATION = [];  % The duration of one single frame, ideally less than 1/60
    MAX_FRAME_SKIP = [];  % The maximum of frame skips allowed if the game runs sluggishly
    
    MainFigureInitPos = [];  % The initial position of the main figure
    MainFigureSize = [];  % The size of the figure
    MainAxesInitPos = []; % The initial position of the axes IN the figure
    
    % Handles
    MainFigureHdl = [];
    MainAxesHdl = [];
    SceneHandle = [];
    
    % Keyboard-related variables
    KeyStatus = [];
    LastKeyStatus = [];
    KeyNames = [];
    
    % Variables for Debugging
    ShowFPS = false;
    SHOWFPS_FRAMES = 60;
    
    % Collision Flags
    CloseReq = false;
    
    key = [];
    
    % player character
    
    % Canvas
    EmptyCanvas = [];
    CurCanvas = [];
    
    %% Raycasting & Rendering related variables
    view = [];
    scr_res = [];
    wall_texture = [];   % A 64 x whatever strip that contains all possible wall textures
    cmap = [];          % The global palette
    
    texture_wid = [];
    texture_hgt = [];
    
    xx = [];
    yy = [];
    yy_p = [];
    yy_text = [];
    yy_standard = [];
    apparent_h = [];
    h_scale_factor = [];
    cam_angle_comp = [];
    ones200 = [];       % ones(1,200)
    DEG2RAD = pi/180;
    screenCenter = [];
    use_mouse = true;
    %% Initialization
    initVariable;
    initWindow;
    
    StageList = {'floor1'};
    
    %% Main Game Cycle
    for i_stage = 1:length(StageList)
        %% Load the current stage
        StageTemp = load('WOLF_WAD.mat', StageList{i_stage});
        CurrentStage = StageTemp.(StageList{i_stage});
        
        %% Setup the map and the walls
        world = CurrentStage.world;
        world_size =CurrentStage.world_size;
        wall = CurrentStage.wall;
        wallD = CurrentStage.wallD;
        wallU = CurrentStage.wallU;
        wallL = CurrentStage.wallL;
        wallR = CurrentStage.wallR;
        readTextures;
        colormap(cmap);
        pos = CurrentStage.init_pos;
        facing = CurrentStage.init_facing;
        
        %% Main Game Loop for each stage
        
        frame_updated = false;   % Whether the world is actually updated
        
        CurrentFrameNo = 0;      % the number of the current frame
        
        if ShowFPS
            fps_text_handle = text(10,10, 'FPS:60.0');
            var_text_handle = text(10,20, 'Var = '); % Display a variable
            total_frame_update = 0;
        end
        prompt_text_handle = text(30,80,'','Color',[1 1 1],...
            'FontSize',24,...
            'HorizontalAlignment','left');
        
        stageStartTime = tic;
        c = stageStartTime;
        FPS_lastTime = toc(stageStartTime);
        terminateFlag = false;
        while 1
            loops = 0;
            curTime = toc(stageStartTime);
            while (curTime >= ((CurrentFrameNo) * FRAME_DURATION) && loops < MAX_FRAME_SKIP)
                %% Process Player
                if ~terminateFlag
                    [pos, facing] = processPlayer(pos, facing);
                    [dist_all, wall_type, all_walls] = stl_RayCasting(pos, facing);
                end
                
                CurrentFrameNo = CurrentFrameNo + 1;
                loops = loops + 1;
                frame_updated = true;
            end
            
            %% Redraw the frame if the world has been processed
            if frame_updated
                renderScene(dist_all, wall_type, all_walls);
                drawnow;
                c = toc(stageStartTime);
                frame_updated = false;
                if ShowFPS
                    total_frame_update = total_frame_update + 1;
                    %                 set(fps_text_handle, 'String',sprintf('%.2f',1./b),'Position', [newXLim(1)+10,10]);
                    varname = 'view.angle';%'Mario.curFrame';
                    if mod(total_frame_update,SHOWFPS_FRAMES) == 0 % If time to update fps
                        set(fps_text_handle, 'String',sprintf('FPS: %.2f',SHOWFPS_FRAMES./(c-FPS_lastTime)));
                        FPS_lastTime = toc(stageStartTime);
                    end
                    set(var_text_handle, 'String', sprintf('%s = %.2f', varname, eval(varname)));
                end
            end
            if CloseReq
                delete(MainFigureHdl);
                clear all;
                return;
            end
        end
    end
catch err
    delete(MainFigureHdl);
    rethrow(err);
end
%% ---------------- Regular Subfunctions ----------------------------------

%% Initializations

    function initVariable()
        % initVariable - initialize variables
        
        MainAxesSize = [320 200];
        
        maxWinSize = [800 600];
        
        FPS = 60;
        
        FRAME_DURATION = 1./FPS;
        MAX_FRAME_SKIP = 5;
        %         DEFAULT_FRAME_SKIP = 2;
        MainFigureSize = MainAxesSize .* 2;
        maxReal2max = max(MainFigureSize./maxWinSize);  % 
        if maxReal2max > 1  % at least one of them exceeds the maximum window
            MainFigureSize = MainFigureSize ./ maxReal2max;
        end
        MainFigureInitPos = [300 50];
        MainAxesInitPos = [0 0];
        
        KeyNames = {'w','s','a','d','leftarrow','rightarrow','space','return','escape'};
        %         KeyFuncs = {'up','down','left','right','sprint','jump','start','select'};
        KeyStatus = false(1, length(KeyNames));
        LastKeyStatus = KeyStatus;
        ShowFPS = true;
        
        %         EmptyCanvas = uint8(zeros(MainAxesSize([2 1])));
        %         CurCanvas = uint8(zeros(MainAxesSize([2 1])));
        
        %
        view.angle = 90;
        view.dist = 0.25;
        h_scale_factor =  tan(DEG2RAD * view.angle /2);
        view.plane_size = 2 * view.dist *h_scale_factor;
        
        % wolfview = viewdef; % Get the 3D viewing setting
        view.resx = MainAxesSize(1); % 320 pixels
        view.resy = MainAxesSize(2);
        
        scr_res = -view.resx/2:view.resx/2-1;  % number pixels from -160-159
        
        texture_wid = 64;
        texture_hgt = 64;
        % The angle of each ray relative to the facing vector
        cam_angle_comp = atan((scr_res+0.5) / view.resx * view.plane_size./view.dist);
        
        [xx, yy] = meshgrid(1:view.resx,1:view.resy);
        yy_p = (yy(:,:) - view.resy/2)./(view.resy/2);
        [~, yy_text] = meshgrid(1:view.resx, linspace(1,texture_hgt,view.resy));
        [~, yy_standard] = meshgrid(1:view.resx, linspace(0,1,view.resy));
        yy_text = round(yy_text);
        EmptyCanvas = uint8([ones(view.resy./2, view.resx);zeros(view.resy./2, view.resx)]);
        CurCanvas = uint8(zeros(view.resy, view.resx));
        
        ones200 = ones(1,view.resy);
    end

    function readTextures()
        %         for i = 0:65
        %             [cur_wall_texture, cur_wall_map] = imread(['Spear' filesep num2str(i) '.bmp']);
        %             wall_texture(:,(i*64+1):(i+1)*64,:) = ind2rgb(cur_wall_texture, cur_wall_map);
        %         end
        wall_texture = load('WOLF_WALLS.mat');
        wall_texture = wall_texture.wall_texture(:,1:max(wall(:))*texture_wid,:);
        floor_color = [0.7 0.7 0.7];
        ceiling_color = [0.5 0.5 0.5];
        
        [wall_texture, cmap] = rgb2ind(wall_texture,254);
        wall_texture = wall_texture + 2;
        cmap = [floor_color; ceiling_color; cmap];
    end

    function initWindow()
        % initWindow - initialize the main window, axes and image objects
        MainFigureHdl = figure('Name', ['Wolf3D MAT ' WolfVer], ...
            'NumberTitle' ,'off', ...
            'Units', 'pixels', ...
            'Position', [MainFigureInitPos, MainFigureSize], ...
            'MenuBar', 'figure', ...
            'Renderer', 'Painter',...\
            'UserData', 'stl_mario_main',...
            'KeyPressFcn', @stl_KeyDown,...
            'KeyReleaseFcn', @stl_KeyUp, ...
            'WindowScrollWheelFcn', @stl_MouseWheel); %,...
        %             'CloseRequestFcn', @stl_CloseReqFcn);
        MainAxesHdl = axes('Parent', MainFigureHdl, ...
            'Units', 'normalized',...
            'Position', [MainAxesInitPos, 1-MainAxesInitPos.*2], ...
            'color', [0 0 0], ...
            'XLim', [0 MainAxesSize(1)]-0.5, ...
            'YLim', [0 MainAxesSize(2)]-0.5, ...
            'YDir', 'reverse', ...
            'NextPlot', 'add', ...
            'Visible', 'on', ...
            'XTick',[], ...
            'YTick',[]);
        SceneHandle = image(0, 0, [],...
            'Parent', MainAxesHdl,...
            'Visible', 'on');

        screenCenter = get(0,'ScreenSize');
        screenCenter = screenCenter(3:4)./2;
        set(0,'PointerLocation',screenCenter)
    end

%% Game logics
    function [pos, facing] = processPlayer(pos, facing)
        %
        fw_vec = [cos(facing * DEG2RAD), sin(facing * DEG2RAD)] * 2/FPS;
        left_vec = fw_vec * [0 -1;1 0]; % rotate -90 deg
        rot_angle = 60/FPS;
        new_pos = pos;
        if KeyStatus(1)
            new_pos = pos + fw_vec;
        end
        if KeyStatus(2)
            new_pos = pos - fw_vec;
        end
        if KeyStatus(3)
            new_pos = pos + left_vec;
        end
        if KeyStatus(4)
            new_pos = pos - left_vec;
        end
        if KeyStatus(5)
            facing = facing - rot_angle;
        end
        if KeyStatus(6)
            facing = facing + rot_angle;
        end
        
        new_pos(new_pos<1.5) = 1.5;
        if new_pos(1) > world_size(2)-1.5
            new_pos(1) = world_size(2) - 1.5;
        end
        
        if new_pos(2) > world_size(1)-1.5
            new_pos(2) = world_size(1) - 1.5;
        end
        
        if use_mouse
            curMousePos = get(0, 'PointerLocation');
            curMouseDispl = curMousePos - screenCenter;
            set(0,'PointerLocation',screenCenter)
            facing = facing + curMouseDispl(1);
        end
        facing = mod(facing, 360);
        
        %% Collision Detection
        if false
            pos_dec_flag = false;
            
            if floor(new_pos(1) + 0.5) ~= floor(pos(1) + 0.5) % just passed a centre point horizontally
                % in that case, we would check what would happen if player
                % moves exactly to the centre point
                temp_x_new_pos = [floor(new_pos(1) + 0.5), floor(pos(2)+0.5)];
                x_niche_pos = [floor(temp_x_new_pos + 0.5); ceil(temp_x_new_pos + 0.5)];
                
                temp_niche_pos = world(x_niche_pos(:,2), x_niche_pos(:,1));
                if ~any(temp_niche_pos)  % If it's passable
                    new_pos(1) = temp_x_new_pos(1) - 0.5;
                    new_pos(2) = pos(2);
                    pos_dec_flag = true
                end
            end
            if pos_dec_flag
                pos = new_pos;
                return
            end
            if floor(new_pos(2) + 0.5) ~= floor(pos(2) + 0.5)
                temp_y_new_pos = [floor(pos(1)+0.5), floor(new_pos(2) + 0.5)];
                y_niche_pos = [floor(temp_y_new_pos + 0.5); ceil(temp_y_new_pos + 0.5)];
                
                temp_niche_pos = world(y_niche_pos(:,2), y_niche_pos(:,1));
                if ~any(temp_niche_pos)
                    new_pos(1) = pos(1);
                    new_pos(2) = temp_y_new_pos(2) - 0.5;
                end
            end
            if pos_dec_flag
                pos = new_pos;
                return
            end
            mov_vec = new_pos - pos;
            x_new_pos = [new_pos(1) pos(2)];
            y_new_pos = [pos(1) new_pos(2)];
            
            x_niche_pos = [floor(x_new_pos + 0.5); ceil(x_new_pos + 0.5)];
            y_niche_pos = [floor(y_new_pos + 0.5); ceil(y_new_pos + 0.5)];
            
            x_niche = world(x_niche_pos(:,2),x_niche_pos(:,1));
            y_niche = world(y_niche_pos(:,2),y_niche_pos(:,1));
            if any(x_niche(:)) % && any(y_niche(:))  % Collision on the right side
                if mov_vec(1) > 0
                    new_pos(1) = floor(new_pos(1) + 0.5) - 0.5;
                elseif mov_vec(1) < 0
                    new_pos(1) = ceil(new_pos(1) - 0.5) + 0.5;
                else
                    error('ComeonX');
                end
            end
            
            
            
            if any(y_niche(:))
                if mov_vec(2) > 0
                    new_pos(2) = floor(new_pos(2) + 0.5) - 0.5;
                elseif mov_vec(2) < 0
                    new_pos(2) = ceil(new_pos(2) - 0.5) + 0.5;
                else
                    error('ComeonY');
                end
            end
        end
        pos = new_pos;
        
    end
%% Perform raycasting for a frame

    function [dist_all, int_wall_type, all_walls] = stl_RayCasting(pos, facing)
        
        cur_view.angle = DEG2RAD * (facing) + cam_angle_comp;
        
        % Precalculate all the cos/sin/tans
        cur_view.angle_cos = cos(cur_view.angle);
        cur_view.angle_sin = sin(cur_view.angle);
        cur_view.angle_tan = tan(cur_view.angle);
        
        %% Find intersections on horizontal walls
        faceup = cur_view.angle_sin < 0;        % Those rays that point upward
        facedown = cur_view.angle_sin > 0;      % Those that go downward
        
        nup = sum(faceup);
        ndown = sum(facedown);
        
        %{ player pos
        %  -0---1-2----------------
        %  |  |  |  |
        %  0  1  2  3 ...
        %  |  |  |  |
        %  -1---1-2----------------
        %}
        
        %% UP and DOWN
        % Y coord of the first intersection in the UP and DOWN directions
        yup = ceil(pos(2)-1);    % Ensures correct calculation if
        ydown = floor(pos(2)+1); % player happens to be on a boundary
        
        %The Y coord of the first intersections for ALL rays
        yups = yup(1,ones(1,nup));
        % Calculate the corresponding x coords
        xups = (pos(1) + (yups - pos(2))./cur_view.angle_tan(faceup));
        
        % Do the same for downward rays
        ydowns = ydown(1, ones(1, ndown));
        xdowns = (pos(1) + (ydown - pos(2))./cur_view.angle_tan(facedown));
        
        % The final (x,y) for intersections:
        yhoris = zeros(1, view.resx)+Inf;
        xhoris = zeros(1, view.resx)+Inf;
        wall_horis = zeros(1, view.resx);
        
        % Init the intersection flags
        cur_intersect = false(1,nup);
        cur_non_intersect = ~cur_intersect;
        no_intersect = false(1, nup);
        wall_up = zeros(1,nup);   % The texture id of each intersection
        
        cur_ints_d = false(1, ndown);
        cur_non_ints_d = ~cur_ints_d;
        no_ints_d = ~false(1, ndown);
        wall_down = zeros(1,ndown);
        
        cur_inv_tan = 1./cur_view.angle_tan(faceup);
        cur_inv_tan_d = 1./cur_view.angle_tan(facedown);
        
        %% Process UPWARD first
        
        % Flags that are set to true when the rays intersect with a wall.
        for ii = 1:1
            %tic;
            cur_intersect(:) = false;
            cur_non_intersect(:) = ~cur_intersect(:);
            no_intersect(:) = false;
            k = floor(xups(cur_non_intersect))+1;
            no_intersect(cur_non_intersect) = (yup < 1) | ...          % if y is too small
                (floor(xups(cur_non_intersect))+1 > world_size(2)) | ...   % if x is too large
                (floor(xups(cur_non_intersect))+1 < 1);
            
            %toc;
            
            while 1
                % If a ray is impossible to make an intersection with a horizontal wall
                % then its 'no_intersect' flag will be set to true, treating them as
                % 'already intersected'
                
                no_intersect(cur_non_intersect) = (yup < 1) | ...          % if y is too small
                    (floor(xups(cur_non_intersect))+1 > world_size(2)) | ...   % if x is too large
                    (floor(xups(cur_non_intersect))+1 < 1);                % if x is too small
                
                cur_intersect = cur_intersect | no_intersect;
                cur_non_intersect = ~cur_intersect;
                
                if all(cur_intersect)
                    break;
                end
                
                % Check if the intersection is a wall
                % for all the non-intersected rays
                cur_intersect(cur_non_intersect) = ...
                    world(yup, floor(xups(cur_non_intersect))+1);
                
                % If all rays are 'intersected', then quit.
                %     if all(cur_intersect)
                %         break;
                %     end
                
                % Find those that still aren't intersected
                cur_non_intersect = ~cur_intersect;
                
                %  scan the next possible point
                yup = yup - 1;
                yups(cur_non_intersect) = yup;
                
                % using sign always generates 1 or -1, since xups/xdowns do not include
                % 0 cases.
                xups(cur_non_intersect) = xups(cur_non_intersect) - ...
                    cur_inv_tan(cur_non_intersect);
                %1./cur_tan(cur_non_intersect);
                
                %     no_intersect = xups(floor(xups)+1 > world_size(2)) = 0;
                %     yup(yup < 1) = 1;
            end
            
            xups(no_intersect) = Inf;
            yups(no_intersect) = Inf;
            % Get the texture
            xups_int = floor(xups(~no_intersect));
            ind = world_size(1) * xups_int +yups(~no_intersect); % No + 1
            wall_up(~no_intersect) = (wallD(ind)-1) * texture_wid + ... % The texture index
                floor((xups(~no_intersect) - xups_int) * texture_wid)+1; % The column index
            
            % The wall texture column the rays encounters on their way up
            
            % wallups(~no_intersect) = wall(sub2ind(world_size, yups(~no_intersect),...
            %     ceil(xups(~no_intersect))));
            % xups
            %toc;
            % disp('Up over');
            % visual_raycast(world, pos, xups, yups)
            %% Then process DOWNWARD
            % Flags that are set to true when the rays intersect with a wall.
            cur_ints_d(:) = false;
            cur_non_ints_d(:) = ~cur_ints_d(:);
            no_ints_d(:) = false;
            %tic;
            while 1
                % If a ray is impossible to make an intersection with a horizontal wall
                % then its 'no_ints_d' flag will be set to true, treating them as
                % 'already intersected'
                no_ints_d(cur_non_ints_d) = (ydown > world_size(1)-1) | ...          % if y is too small
                    (floor(xdowns(cur_non_ints_d))+1 > world_size(2)) | ...   % if x is too large
                    (floor(xdowns(cur_non_ints_d))+1 < 1);                % if x is too small
                
                cur_ints_d = cur_ints_d | no_ints_d;
                cur_non_ints_d = ~cur_ints_d;
                if all(cur_ints_d)
                    break;
                end
                
                % Check if the intersection is a wall
                % for all the non-intersected rays
                % For downward rays, 'ydown+1' means takes the
                % block 'below' the intersected wall.
                cur_ints_d(cur_non_ints_d) = ...
                    world(ydown+1, floor(xdowns(cur_non_ints_d))+1);
                
                % If all rays are 'intersected', then quit.
                %     if all(cur_ints_d)
                %         break;
                %     end
                
                % Find those that still aren't intersected
                cur_non_ints_d = ~cur_ints_d;
                
                %  scan the next possible point
                ydown = ydown + 1;
                ydowns(cur_non_ints_d) = ydown;
                xdowns(cur_non_ints_d) = xdowns(cur_non_ints_d) + ...
                    cur_inv_tan_d(cur_non_ints_d);
                %         1./cur_tan_d(cur_non_ints_d);
            end
            xdowns(no_ints_d) = Inf;
            ydowns(no_ints_d) = Inf;
            
            % Get the texture
            % ind = world_size(1)*xdowns_int+ydowns(~no_ints_d)+1;
            % wall_down(~no_ints_d) = wallU(ind);
            % Get the texture
            xdowns_int = floor(xdowns(~no_ints_d));
            ind = world_size(1)*xdowns_int+ydowns(~no_ints_d)+1; % Have to +1 because the block is below the intersection
            wall_down(~no_ints_d) = ((wallU(ind)-1)*texture_wid) + floor((1- xdowns(~no_ints_d) + xdowns_int) * texture_wid)+1; % The wall texture index the rays encounters on their way up
            % if the ray goes down, it means the texture starts from the right. Hence '1- xdowns(~no_ints_d) + xdowns_int'
            
            %toc;
            % visual_raycast(world, pos, xdowns, ydowns)
            
            %% Combine UP and DOWN together
            % xhori(faceup) = xups;
            % yhori(faceup) = yups;
            %
            % xhori(facedown) = xdowns;
            % yhori(facedown) = ydowns;
            
            %% ---------------------------------------
            %% ---------------------------------------
            %% ---------- Now find intersections on Verticle Walls
            %% -----------------------------------------
            %% ------------------------------------------
            %% Find intersections on horizontal walls
            faceleft = cur_view.angle_cos < 0;        % Those rays that point leftward
            faceright = cur_view.angle_cos > 0;      % Those that go rightward
            
            nleft = sum(faceleft);
            nright = sum(faceright);
            
            %% LEFT and RIGHT
            % x coord of the first intersection in the LEFT-RIGHT directions
            xleft = ceil(pos(1)-1);    % Ensures correct calculation if
            xright = floor(pos(1)+1); % player happens to be on a boundary
            
            %The Y coord of the first intersections for ALL leftward rays
            xlefts = xleft(1,ones(1,nleft));
            % Calculate the corresponding x coords
            ylefts = (pos(2) + (xlefts - pos(1)) .* cur_view.angle_tan(faceleft));
            
            % Do the same for rightward rays
            xrights = xright(1, ones(1, nright));
            yrights = (pos(2) + (xrights - pos(1)).*cur_view.angle_tan(faceright));
            
            % The final (x,y) for ALL intersections:
            yverts = zeros(1, view.resx) + Inf;
            xverts = zeros(1, view.resx) + Inf;
            wall_verts = zeros(1, view.resx);
            
            cur_ints_l = false(1,nleft);
            cur_non_ints_l = ~cur_ints_l;
            no_ints_l = false(1, nleft);
            cur_tan_l = cur_view.angle_tan(faceleft);
            wall_left = zeros(1, nleft);
            
            cur_ints_r = false(1, nright);
            cur_non_ints_r = ~cur_ints_r;
            no_ints_r = ~false(1, nright);
            cur_tan_r = cur_view.angle_tan(faceright);
            wall_right = zeros(1, nright);
            
            %% Process LEFTward first
            
            % Flags that are set to true when the rays intersect with a wall.
            cur_ints_l(:) = false;
            cur_non_ints_l(:) = ~cur_ints_l(:);
            no_ints_l(:) = false;
            %tic;
            while 1
                % If a ray is impossible to make an intersection with a horizontal wall
                % then its 'no_intersect' flag will be set to true, treating them as
                % 'already intersected'
                no_ints_l(cur_non_ints_l) = (xleft < 1) | ...          % if x is too small
                    (floor(ylefts(cur_non_ints_l))+1 > world_size(1)) | ...   % if y is too large
                    (floor(ylefts(cur_non_ints_l))+1 < 1);                % if y is too small
                
                cur_ints_l = cur_ints_l | no_ints_l;
                cur_non_ints_l = ~cur_ints_l;
                if all(cur_ints_l)
                    break;
                end
                
                % Check if the intersection is a wall
                % for all the non-intersected rays
                cur_ints_l(cur_non_ints_l) = ...
                    world(floor(ylefts(cur_non_ints_l))+1, xleft);
                
                % If all rays are 'intersected', then quit.
                %     if all(cur_ints_l)
                %         break;
                %     end
                
                % Find those that still aren't intersected
                cur_non_ints_l = ~cur_ints_l;
                
                %  scan the next possible point (the one on the left)
                xleft = xleft - 1;
                xlefts(cur_non_ints_l) = xleft;
                
                % using sign always generates 1 or -1, since there are no
                % 0 cases.
                ylefts(cur_non_ints_l) = ylefts(cur_non_ints_l) -...
                    cur_tan_l(cur_non_ints_l); % + ...
                %sign(cur_view.angle_sin(cur_non_ints_l));
                
                %     no_intersect = ylefts(floor(ylefts)+1 > world_size(2)) = 0;
                %     yup(yup < 1) = 1;
            end
            
            
            
            ylefts(no_ints_l) = Inf;
            xlefts(no_ints_l) = Inf;
            %toc;
            % Get the texture
            ylefts_int = floor(ylefts(~no_ints_l));
            ind = world_size(1) * (xlefts(~no_ints_l)-1) + ylefts_int+1; % -1 for the block on the 'left' of the wall
            ind(ind==0) = 1;
            wall_left(~no_ints_l) = (wallR(ind)-1) * texture_wid + ... % The texture index
                floor((1 - ylefts(~no_ints_l) + ylefts_int) * texture_wid)+1; % The column index
            
            
            % visual_raycast(world, pos, xlefts, ylefts);
            
            %% Then process RIGHTWARD
            
            % Flags that are set to true when the rays intersect with a wall.
            cur_ints_r(:) = false;
            cur_non_ints_r(:) = ~cur_ints_r(:);
            no_ints_r(:) = false;
            %tic;
            while 1
                % If a ray is impossible to make an intersection with a horizontal wall
                % then its 'no_intersect' flag will be set to true, treating them as
                % 'already intersected'
                no_ints_r(cur_non_ints_r) = (xright + 1 > world_size(2)) | ...          % if x is too large
                    (floor(yrights(cur_non_ints_r))+1 > world_size(1)) | ...   % if y is too large
                    (floor(yrights(cur_non_ints_r))+1 < 1);                % if y is too small
                
                cur_ints_r = cur_ints_r | no_ints_r;
                cur_non_ints_r = ~cur_ints_r;
                if all(cur_ints_r)
                    break;
                end
                
                % Check if the intersection is a wall
                % for all the non-intersected rays
                cur_ints_r(cur_non_ints_r) = ...
                    world(floor(yrights(cur_non_ints_r))+1, xright+1);
                
                % If all rays are 'intersected', then quit.
                %     if all(cur_ints_r)
                %         break;
                %     end
                
                % Find those that still aren't intersected
                cur_non_ints_r = ~cur_ints_r;
                
                %  scan the next possible point (the one on the left)
                xright = xright + 1;
                xrights(cur_non_ints_r) = xright;
                
                % using sign always generates 1 or -1, since there are no
                % 0 cases.
                yrights(cur_non_ints_r) = yrights(cur_non_ints_r) +...
                    cur_tan_r(cur_non_ints_r); % + ...
                %sign(cur_view.angle_sin(cur_non_ints_r));
                
                %     no_intersect = ylefts(floor(ylefts)+1 > world_size(2)) = 0;
                %     yup(yup < 1) = 1;
            end
            
            yrights(no_ints_r) = Inf;
            xrights(no_ints_r) = Inf;
            %toc;
            yrights_int = floor(yrights(~no_ints_r));
            ind = world_size(1) * (xrights(~no_ints_r)) + yrights_int + 1 ;
            % has +1 and has no -1 for the block on the 'right' of the wall
            
            
            wall_right(~no_ints_r) = (wallL(ind)-1) * texture_wid + ... % The texture index
                floor((yrights(~no_ints_r) - yrights_int) * texture_wid)+1; % The column index
            
            % visual_raycast(world, pos, xrights, yrights);
            
            %% Combine UP and DOWN together, LEFT and RIGHT together
            xhoris(faceup) = xups;
            yhoris(faceup) = yups;
            
            xhoris(facedown) = xdowns;
            yhoris(facedown) = ydowns;
            
            % wall_up(~no_intersect) = wallD(ind); % The wall texture index the rays encounters on their way up
            % wall_down(~no_ints_d)
            wall_horis(faceup) = wall_up;
            wall_horis(facedown) = wall_down;
            
            xverts(faceleft) = xlefts;
            yverts(faceleft) = ylefts;
            
            xverts(faceright) = xrights;
            yverts(faceright) = yrights;
            
            wall_verts(faceleft) = wall_left;
            wall_verts(faceright) = wall_right;
            
            dist_hori = sqrt((xhoris-pos(1)).^2 + (yhoris-pos(2)).^2);
            dist_vert = sqrt((xverts-pos(1)).^2 + (yverts-pos(2)).^2);
            [dist_all, int_wall_type] = min([dist_hori; dist_vert]);
            all_walls = [wall_horis, wall_verts];
        end
    end

    function renderScene(dist_all, int_wall_type, all_walls)
        % Render the final image !!!!1
        % The apparent height of each wall column on screen (in pixels);
%         apparent_h = round(view.resx.*view.plane_size./(dist_all)./cos(cam_angle_comp));
        h_scale_factor = tan(DEG2RAD * view.angle ./2);
        apparent_h = round(view.resx./2./h_scale_factor./(dist_all)./cos(cam_angle_comp));

        % The y coords where the wall columns start
        start_y_wallcols = ceil((view.resy - apparent_h)./2);
        apparent_h_ary = apparent_h(ones200,:);
        
        % All the wall columns to be mapped on the actual walls
        % texture_strip = [rand(64, view.resx); zeros(1, view.resx)+NaN];
        texture_strip = wall_texture(:,all_walls([1:view.resx] +(int_wall_type-1)*view.resx));
        
        
        full_walls = start_y_wallcols >=0;
        all_yy = round(yy(:,full_walls) + (apparent_h_ary(:,full_walls)-view.resy)./2 .* yy_p(:,full_walls));
        all_yy(all_yy>view.resy) = view.resy;
        all_yy(all_yy<1) = 1;
        
        all_xx = xx(:,full_walls);
        all_yy_text = round(yy_text(:, full_walls));
        all_text_xx = xx(:,~full_walls);
        
        cropped_yy_standard = yy_standard(:, ~full_walls);
        text_strip_stary_y =  repmat(round((-start_y_wallcols)*texture_hgt./apparent_h),[view.resy 1]); % Where the texture y starts
        cropped_yy_text = round(cropped_yy_standard .* ((texture_hgt-1) - 2*text_strip_stary_y(:,~full_walls)) + text_strip_stary_y(:,~full_walls)+1);
        cropped_yy_text(cropped_yy_text<1) = 1;
        cropped_yy_text(cropped_yy_text > texture_hgt) = texture_hgt;
        
        CurCanvas = EmptyCanvas;
        CurCanvas((all_xx(:)-1)*view.resy + all_yy(:)) = texture_strip((all_xx(:)-1)*texture_hgt + all_yy_text(:));
        CurCanvas(:, ~full_walls) = texture_strip((all_text_xx-1)*texture_hgt + cropped_yy_text);
        
        set(SceneHandle, 'CData', CurCanvas);
    end

%% Callback functions
    function stl_KeyUp(hObject, eventdata, handles)
        LastKeyStatus = KeyStatus;
        
        key = get(hObject,'CurrentKey');
        KeyStatus = (~strcmp(key, KeyNames) & LastKeyStatus);
        
    end
    function stl_KeyDown(hObject, eventdata, handles)
        LastKeyStatus = KeyStatus;
        key = get(hObject,'CurrentKey');
        
        KeyStatus = (strcmp(key, KeyNames) | LastKeyStatus);
        
        if KeyStatus(8)
            use_mouse = ~use_mouse;
        end
        if KeyStatus(9)
            CloseReq = true;
        end
    end
    function stl_MouseWheel(hObject, eventdata, handles)
        view.angle =  view.angle + eventdata.VerticalScrollCount*1.5;
        view.angle = min(max(view.angle, 10),90);
       
        % Refresh some variables
        h_scale_factor =  tan(DEG2RAD * view.angle /2);
        view.plane_size = 2 * view.dist *h_scale_factor;
        cam_angle_comp = atan((scr_res+0.5) / view.resx * view.plane_size./view.dist);
    end

    function stl_CloseReqFcn(hObject, eventdata, handles)
        CloseReq = true;
    end

end


Contact us