├── crate.jpg ├── README ├── game_core.erl ├── lesson01.erl ├── lesson02.erl ├── lesson03.erl ├── lesson04.erl ├── lesson05.erl ├── lesson06.erl ├── lesson12.erl └── lesson07.erl /crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asceth/nehe_erlang/HEAD/crate.jpg -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This repo holds the lesson's from NeHe's OpenGL tutorials for Erlang: 2 | 3 | http://nehe.gamedev.net/ 4 | 5 | 6 | You must have R13B and a working wx erlang library (which by default comes with R13B). 7 | You can type in wx:new(). to see if it is working for you in the erlang shell. 8 | 9 | 10 | Running the lessons: 11 | 12 | erl 13 | > GC = game_core:start_link(). 14 | > game_core:load(GC, lesson01). 15 | 16 | Enjoy lesson 01... 17 | 18 | > game_core:unload(GC) % unloads currently loaded lesson 19 | > game_core:load(GC, lesson02) % loads next lesson 20 | 21 | 22 | An interesting thing to do is try to modify a currently loaded lesson's 23 | source code draw method and compile it in the shell. Initial attempts have shown 24 | immediate updates with no crashing which can lead up to interactive programming... :) 25 | 26 | -------------------------------------------------------------------------------- /game_core.erl: -------------------------------------------------------------------------------- 1 | -module(game_core). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([start_link/0, start_link/1, init/1, code_change/3, handle_info/2, handle_event/2, handle_call/3, terminate/2]). 6 | -export([load/2, unload/1, shutdown/1]). 7 | 8 | -include_lib("wx/include/wx.hrl"). 9 | -include_lib("wx/include/gl.hrl"). 10 | -include_lib("wx/include/glu.hrl"). 11 | 12 | -record(state, { 13 | win, 14 | object 15 | }). 16 | 17 | start_link() -> 18 | start_link([]). 19 | 20 | start_link(Config) -> 21 | wx_object:start_link(?MODULE, Config, []). 22 | 23 | init(Config) -> 24 | wx:new(Config), 25 | process_flag(trap_exit, true), 26 | 27 | Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Game Core", [{size, {300, 300}}]), 28 | wxFrame:show(Frame), 29 | {Frame, #state{win = Frame}}. 30 | 31 | load(Ref, Module) -> 32 | wx_object:call(Ref, {load, Module}). 33 | unload(Ref) -> 34 | wx_object:call(Ref, unload). 35 | shutdown(Ref) -> 36 | wx_object:call(Ref, stop). 37 | 38 | handle_info({'EXIT', _, wx_deleted}, State) -> 39 | {noreply, State}; 40 | handle_info({'EXIT', _, normal}, State) -> 41 | {noreply, State}; 42 | handle_info(Msg, State) -> 43 | io:format("Info: ~p~n", [Msg]), 44 | {noreply, State}. 45 | 46 | handle_call({load, Module}, _From, State) -> 47 | Ref = Module:start([{parent, State#state.win}, {size, wxWindow:getClientSize(State#state.win)}]), 48 | {reply, Ref, State#state{object=Ref}}; 49 | handle_call(unload, _From, State) -> 50 | wx_object:get_pid(State#state.object) ! stop, 51 | {reply, ok, State#state{object=undefined}}; 52 | handle_call(stop, _From, State) -> 53 | {stop, normal, State}; 54 | handle_call(Msg, _From, State) -> 55 | io:format("Call: ~p~n", [Msg]), 56 | {reply, ok, State}. 57 | 58 | 59 | handle_event(#wx{event=#wxClose{}}, State = #state{win = Frame}) -> 60 | io:format("~p Closing window ~n", [self()]), 61 | ok = wxFrame:setStatusText(Frame, "Closing...", []), 62 | {stop, normal, State}; 63 | handle_event(Ev, State) -> 64 | io:format("~p Event: ~p~n", [?MODULE, Ev]), 65 | {noreply, State}. 66 | 67 | code_change(_, _, State) -> 68 | {stop, not_yet_implemented, State}. 69 | 70 | terminate(_Reason, _State) -> 71 | wx:destroy(). 72 | 73 | 74 | -------------------------------------------------------------------------------- /lesson01.erl: -------------------------------------------------------------------------------- 1 | -module(lesson01). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time 19 | }). 20 | 21 | start(Config) -> 22 | wx_object:start_link(?MODULE, Config, []). 23 | 24 | init(Config) -> 25 | wx:batch(fun() -> do_init(Config) end). 26 | 27 | do_init(Config) -> 28 | Parent = proplists:get_value(parent, Config), 29 | Size = proplists:get_value(size, Config), 30 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 31 | GLAttrib = [{attribList, [?WX_GL_RGBA, 32 | ?WX_GL_DOUBLEBUFFER, 33 | ?WX_GL_MIN_RED, 8, 34 | ?WX_GL_MIN_GREEN, 8, 35 | ?WX_GL_MIN_BLUE, 8, 36 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 37 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 38 | wxGLCanvas:connect(Canvas, size), 39 | wxWindow:hide(Parent), 40 | wxWindow:reparent(Canvas, Parent), 41 | wxWindow:show(Parent), 42 | wxGLCanvas:setCurrent(Canvas), 43 | setup_gl(Canvas), 44 | Timer = timer:send_interval(20, self(), update), 45 | 46 | {Parent, #state{parent = Parent, config = Config, canvas = Canvas, timer = Timer}}. 47 | 48 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 49 | case W =:= 0 orelse H =:= 0 of 50 | true -> skip; 51 | _ -> 52 | gl:viewport(0, 0, W, H), 53 | gl:matrixMode(?GL_PROJECTION), 54 | gl:loadIdentity(), 55 | glu:perspective(45.0, W / H, 0.1, 100.0), 56 | gl:matrixMode(?GL_MODELVIEW), 57 | gl:loadIdentity() 58 | end, 59 | {noreply, State}. 60 | 61 | handle_info(update, State) -> 62 | wx:batch(fun() -> render(State) end), 63 | {noreply, State}; 64 | 65 | handle_info(stop, State) -> 66 | timer:cancel(State#state.timer), 67 | catch wxGLCanvas:destroy(State#state.canvas), 68 | {stop, normal, State}. 69 | 70 | handle_call(Msg, _From, State) -> 71 | io:format("Call: ~p~n", [Msg]), 72 | {reply, ok, State}. 73 | 74 | code_change(_, _, State) -> 75 | {stop, not_yet_implemented, State}. 76 | 77 | terminate(_Reason, State) -> 78 | catch wxGLCanvas:destroy(State#state.canvas), 79 | timer:cancel(State#state.timer), 80 | timer:sleep(300). 81 | 82 | 83 | setup_gl(Win) -> 84 | {_W, _H} = wxWindow:getClientSize(Win), 85 | gl:shadeModel(?GL_SMOOTH), 86 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 87 | gl:clearDepth(1.0), 88 | gl:enable(?GL_DEPTH_TEST), 89 | gl:depthFunc(?GL_LEQUAL), 90 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 91 | ok. 92 | 93 | render(#state{parent = _Window, canvas = Canvas} = _State) -> 94 | draw(), 95 | wxGLCanvas:swapBuffers(Canvas). 96 | 97 | draw() -> 98 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 99 | gl:loadIdentity(), 100 | ok. 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /lesson02.erl: -------------------------------------------------------------------------------- 1 | -module(lesson02). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time 19 | }). 20 | 21 | start(Config) -> 22 | wx_object:start_link(?MODULE, Config, []). 23 | 24 | init(Config) -> 25 | wx:batch(fun() -> do_init(Config) end). 26 | 27 | do_init(Config) -> 28 | Parent = proplists:get_value(parent, Config), 29 | Size = proplists:get_value(size, Config), 30 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 31 | GLAttrib = [{attribList, [?WX_GL_RGBA, 32 | ?WX_GL_DOUBLEBUFFER, 33 | ?WX_GL_MIN_RED, 8, 34 | ?WX_GL_MIN_GREEN, 8, 35 | ?WX_GL_MIN_BLUE, 8, 36 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 37 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 38 | wxGLCanvas:connect(Canvas, size), 39 | wxWindow:hide(Parent), 40 | wxWindow:reparent(Canvas, Parent), 41 | wxWindow:show(Parent), 42 | wxGLCanvas:setCurrent(Canvas), 43 | setup_gl(Canvas), 44 | Timer = timer:send_interval(20, self(), update), 45 | 46 | {Parent, #state{parent = Parent, config = Config, canvas = Canvas, timer = Timer}}. 47 | 48 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 49 | case W =:= 0 orelse H =:= 0 of 50 | true -> skip; 51 | _ -> 52 | resize_gl_scene(W, H) 53 | end, 54 | {noreply, State}. 55 | 56 | handle_info(update, State) -> 57 | wx:batch(fun() -> render(State) end), 58 | {noreply, State}; 59 | 60 | handle_info(stop, State) -> 61 | timer:cancel(State#state.timer), 62 | catch wxGLCanvas:destroy(State#state.canvas), 63 | {stop, normal, State}. 64 | 65 | handle_call(Msg, _From, State) -> 66 | io:format("Call: ~p~n", [Msg]), 67 | {reply, ok, State}. 68 | 69 | code_change(_, _, State) -> 70 | {stop, not_yet_implemented, State}. 71 | 72 | terminate(_Reason, State) -> 73 | catch wxGLCanvas:destroy(State#state.canvas), 74 | timer:cancel(State#state.timer), 75 | timer:sleep(300). 76 | 77 | resize_gl_scene(Width, Height) -> 78 | gl:viewport(0, 0, Width, Height), 79 | gl:matrixMode(?GL_PROJECTION), 80 | gl:loadIdentity(), 81 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 82 | gl:matrixMode(?GL_MODELVIEW), 83 | gl:loadIdentity(). 84 | 85 | setup_gl(Win) -> 86 | {W, H} = wxWindow:getClientSize(Win), 87 | resize_gl_scene(W, H), 88 | gl:shadeModel(?GL_SMOOTH), 89 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 90 | gl:clearDepth(1.0), 91 | gl:enable(?GL_DEPTH_TEST), 92 | gl:depthFunc(?GL_LEQUAL), 93 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 94 | ok. 95 | 96 | render(#state{parent = _Window, canvas = Canvas} = _State) -> 97 | draw(), 98 | wxGLCanvas:swapBuffers(Canvas). 99 | 100 | draw() -> 101 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 102 | gl:loadIdentity(), 103 | gl:translatef(-1.5, 0.0, -6.0), 104 | gl:'begin'(?GL_TRIANGLES), 105 | gl:vertex3f(0.0, 1.0, 0.0), 106 | gl:vertex3f(-1.0, -1.0, 0.0), 107 | gl:vertex3f(1.0, -1.0, 0.0), 108 | gl:'end'(), 109 | 110 | gl:translatef(3.0, 0.0, 0.0), 111 | gl:'begin'(?GL_QUADS), 112 | gl:vertex3f(-1.0, 1.0, 0.0), 113 | gl:vertex3f( 1.0, 1.0, 0.0), 114 | gl:vertex3f( 1.0, -1.0, 0.0), 115 | gl:vertex3f(-1.0, -1.0, 0.0), 116 | gl:'end'(), 117 | ok. 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /lesson03.erl: -------------------------------------------------------------------------------- 1 | -module(lesson03). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time 19 | }). 20 | 21 | start(Config) -> 22 | wx_object:start_link(?MODULE, Config, []). 23 | 24 | init(Config) -> 25 | wx:batch(fun() -> do_init(Config) end). 26 | 27 | do_init(Config) -> 28 | Parent = proplists:get_value(parent, Config), 29 | Size = proplists:get_value(size, Config), 30 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 31 | GLAttrib = [{attribList, [?WX_GL_RGBA, 32 | ?WX_GL_DOUBLEBUFFER, 33 | ?WX_GL_MIN_RED, 8, 34 | ?WX_GL_MIN_GREEN, 8, 35 | ?WX_GL_MIN_BLUE, 8, 36 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 37 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 38 | wxGLCanvas:connect(Canvas, size), 39 | wxWindow:hide(Parent), 40 | wxWindow:reparent(Canvas, Parent), 41 | wxWindow:show(Parent), 42 | wxGLCanvas:setCurrent(Canvas), 43 | setup_gl(Canvas), 44 | Timer = timer:send_interval(20, self(), update), 45 | 46 | {Parent, #state{parent = Parent, config = Config, canvas = Canvas, timer = Timer}}. 47 | 48 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 49 | case W =:= 0 orelse H =:= 0 of 50 | true -> skip; 51 | _ -> 52 | resize_gl_scene(W, H) 53 | end, 54 | {noreply, State}. 55 | 56 | handle_info(update, State) -> 57 | wx:batch(fun() -> render(State) end), 58 | {noreply, State}; 59 | 60 | handle_info(stop, State) -> 61 | timer:cancel(State#state.timer), 62 | catch wxGLCanvas:destroy(State#state.canvas), 63 | {stop, normal, State}. 64 | 65 | handle_call(Msg, _From, State) -> 66 | io:format("Call: ~p~n", [Msg]), 67 | {reply, ok, State}. 68 | 69 | code_change(_, _, State) -> 70 | {stop, not_yet_implemented, State}. 71 | 72 | terminate(_Reason, State) -> 73 | catch wxGLCanvas:destroy(State#state.canvas), 74 | timer:cancel(State#state.timer), 75 | timer:sleep(300). 76 | 77 | resize_gl_scene(Width, Height) -> 78 | gl:viewport(0, 0, Width, Height), 79 | gl:matrixMode(?GL_PROJECTION), 80 | gl:loadIdentity(), 81 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 82 | gl:matrixMode(?GL_MODELVIEW), 83 | gl:loadIdentity(). 84 | 85 | setup_gl(Win) -> 86 | {W, H} = wxWindow:getClientSize(Win), 87 | resize_gl_scene(W, H), 88 | gl:shadeModel(?GL_SMOOTH), 89 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 90 | gl:clearDepth(1.0), 91 | gl:enable(?GL_DEPTH_TEST), 92 | gl:depthFunc(?GL_LEQUAL), 93 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 94 | ok. 95 | 96 | render(#state{parent = _Window, canvas = Canvas} = _State) -> 97 | draw(), 98 | wxGLCanvas:swapBuffers(Canvas). 99 | 100 | draw() -> 101 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 102 | gl:loadIdentity(), 103 | gl:translatef(-1.5, 0.0, -6.0), 104 | gl:'begin'(?GL_TRIANGLES), 105 | 106 | gl:color3f(1.0, 0.0, 0.0), 107 | gl:vertex3f(0.0, 1.0, 0.0), 108 | 109 | gl:color3f(0.0, 1.0, 0.0), 110 | gl:vertex3f(-1.0, -1.0, 0.0), 111 | 112 | gl:color3f(0.0, 0.0, 1.0), 113 | gl:vertex3f(1.0, -1.0, 0.0), 114 | 115 | gl:'end'(), 116 | 117 | gl:translatef(3.0, 0.0, 0.0), 118 | 119 | gl:'begin'(?GL_QUADS), 120 | gl:color3f(0.5, 0.5, 1.0), 121 | gl:vertex3f(-1.0, 1.0, 0.0), 122 | gl:vertex3f( 1.0, 1.0, 0.0), 123 | gl:vertex3f( 1.0, -1.0, 0.0), 124 | gl:vertex3f(-1.0, -1.0, 0.0), 125 | gl:'end'(), 126 | 127 | ok. 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /lesson04.erl: -------------------------------------------------------------------------------- 1 | -module(lesson04). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time, 19 | triangle_rot = 10, 20 | square_rot = 10 21 | }). 22 | 23 | start(Config) -> 24 | wx_object:start_link(?MODULE, Config, []). 25 | 26 | init(Config) -> 27 | wx:batch(fun() -> do_init(Config) end). 28 | 29 | do_init(Config) -> 30 | Parent = proplists:get_value(parent, Config), 31 | Size = proplists:get_value(size, Config), 32 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 33 | GLAttrib = [{attribList, [?WX_GL_RGBA, 34 | ?WX_GL_DOUBLEBUFFER, 35 | ?WX_GL_MIN_RED, 8, 36 | ?WX_GL_MIN_GREEN, 8, 37 | ?WX_GL_MIN_BLUE, 8, 38 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 39 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 40 | wxGLCanvas:connect(Canvas, size), 41 | wxWindow:hide(Parent), 42 | wxWindow:reparent(Canvas, Parent), 43 | wxWindow:show(Parent), 44 | wxGLCanvas:setCurrent(Canvas), 45 | setup_gl(Canvas), 46 | Timer = timer:send_interval(20, self(), update), 47 | 48 | {Parent, #state{parent = Parent, config = Config, canvas = Canvas, timer = Timer}}. 49 | 50 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 51 | case W =:= 0 orelse H =:= 0 of 52 | true -> skip; 53 | _ -> 54 | resize_gl_scene(W, H) 55 | end, 56 | {noreply, State}. 57 | 58 | handle_info(update, State) -> 59 | NewState = wx:batch(fun() -> render(State) end), 60 | {noreply, NewState}; 61 | 62 | handle_info(stop, State) -> 63 | timer:cancel(State#state.timer), 64 | catch wxGLCanvas:destroy(State#state.canvas), 65 | {stop, normal, State}. 66 | 67 | handle_call(Msg, _From, State) -> 68 | io:format("Call: ~p~n", [Msg]), 69 | {reply, ok, State}. 70 | 71 | code_change(_, _, State) -> 72 | {stop, not_yet_implemented, State}. 73 | 74 | terminate(_Reason, State) -> 75 | catch wxGLCanvas:destroy(State#state.canvas), 76 | timer:cancel(State#state.timer), 77 | timer:sleep(300). 78 | 79 | resize_gl_scene(Width, Height) -> 80 | gl:viewport(0, 0, Width, Height), 81 | gl:matrixMode(?GL_PROJECTION), 82 | gl:loadIdentity(), 83 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 84 | gl:matrixMode(?GL_MODELVIEW), 85 | gl:loadIdentity(). 86 | 87 | setup_gl(Win) -> 88 | {W, H} = wxWindow:getClientSize(Win), 89 | resize_gl_scene(W, H), 90 | gl:shadeModel(?GL_SMOOTH), 91 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 92 | gl:clearDepth(1.0), 93 | gl:enable(?GL_DEPTH_TEST), 94 | gl:depthFunc(?GL_LEQUAL), 95 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 96 | ok. 97 | 98 | render(#state{parent = _Window, canvas = Canvas} = State) -> 99 | NewState = draw(State), 100 | wxGLCanvas:swapBuffers(Canvas), 101 | NewState. 102 | 103 | draw(#state{triangle_rot = TriangleRot, square_rot = SquareRot} = State) -> 104 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 105 | gl:loadIdentity(), 106 | gl:translatef(-1.5, 0.0, -6.0), 107 | 108 | gl:rotatef(TriangleRot, 0.0, 1.0, 0.0), 109 | gl:'begin'(?GL_TRIANGLES), 110 | 111 | gl:color3f(1.0, 0.0, 0.0), 112 | gl:vertex3f(0.0, 1.0, 0.0), 113 | 114 | gl:color3f(0.0, 1.0, 0.0), 115 | gl:vertex3f(-1.0, -1.0, 0.0), 116 | 117 | gl:color3f(0.0, 0.0, 1.0), 118 | gl:vertex3f(1.0, -1.0, 0.0), 119 | 120 | gl:'end'(), 121 | 122 | gl:loadIdentity(), 123 | gl:translatef(1.5, 0.0, -6.0), 124 | 125 | gl:rotatef(SquareRot, 1.0, 0.0, 0.0), 126 | gl:'begin'(?GL_QUADS), 127 | gl:color3f(0.5, 0.5, 1.0), 128 | gl:vertex3f(-1.0, 1.0, 0.0), 129 | gl:vertex3f( 1.0, 1.0, 0.0), 130 | gl:vertex3f( 1.0, -1.0, 0.0), 131 | gl:vertex3f(-1.0, -1.0, 0.0), 132 | gl:'end'(), 133 | 134 | State#state{triangle_rot = TriangleRot + 0.2, square_rot = SquareRot - 0.15}. 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /lesson05.erl: -------------------------------------------------------------------------------- 1 | -module(lesson05). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time, 19 | triangle_rot = 10, 20 | square_rot = 10 21 | }). 22 | 23 | start(Config) -> 24 | wx_object:start_link(?MODULE, Config, []). 25 | 26 | init(Config) -> 27 | wx:batch(fun() -> do_init(Config) end). 28 | 29 | do_init(Config) -> 30 | Parent = proplists:get_value(parent, Config), 31 | Size = proplists:get_value(size, Config), 32 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 33 | GLAttrib = [{attribList, [?WX_GL_RGBA, 34 | ?WX_GL_DOUBLEBUFFER, 35 | ?WX_GL_MIN_RED, 8, 36 | ?WX_GL_MIN_GREEN, 8, 37 | ?WX_GL_MIN_BLUE, 8, 38 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 39 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 40 | wxGLCanvas:connect(Canvas, size), 41 | wxWindow:hide(Parent), 42 | wxWindow:reparent(Canvas, Parent), 43 | wxWindow:show(Parent), 44 | wxGLCanvas:setCurrent(Canvas), 45 | setup_gl(Canvas), 46 | Timer = timer:send_interval(20, self(), update), 47 | 48 | {Parent, #state{parent = Parent, config = Config, canvas = Canvas, timer = Timer}}. 49 | 50 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 51 | case W =:= 0 orelse H =:= 0 of 52 | true -> skip; 53 | _ -> 54 | resize_gl_scene(W, H) 55 | end, 56 | {noreply, State}. 57 | 58 | handle_info(update, State) -> 59 | NewState = wx:batch(fun() -> render(State) end), 60 | {noreply, NewState}; 61 | 62 | handle_info(stop, State) -> 63 | timer:cancel(State#state.timer), 64 | catch wxGLCanvas:destroy(State#state.canvas), 65 | {stop, normal, State}. 66 | 67 | handle_call(Msg, _From, State) -> 68 | io:format("Call: ~p~n", [Msg]), 69 | {reply, ok, State}. 70 | 71 | code_change(_, _, State) -> 72 | {stop, not_yet_implemented, State}. 73 | 74 | terminate(_Reason, State) -> 75 | catch wxGLCanvas:destroy(State#state.canvas), 76 | timer:cancel(State#state.timer), 77 | timer:sleep(300). 78 | 79 | resize_gl_scene(Width, Height) -> 80 | gl:viewport(0, 0, Width, Height), 81 | gl:matrixMode(?GL_PROJECTION), 82 | gl:loadIdentity(), 83 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 84 | gl:matrixMode(?GL_MODELVIEW), 85 | gl:loadIdentity(). 86 | 87 | setup_gl(Win) -> 88 | {W, H} = wxWindow:getClientSize(Win), 89 | resize_gl_scene(W, H), 90 | gl:shadeModel(?GL_SMOOTH), 91 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 92 | gl:clearDepth(1.0), 93 | gl:enable(?GL_DEPTH_TEST), 94 | gl:depthFunc(?GL_LEQUAL), 95 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 96 | ok. 97 | 98 | render(#state{parent = _Window, canvas = Canvas} = State) -> 99 | NewState = draw(State), 100 | wxGLCanvas:swapBuffers(Canvas), 101 | NewState. 102 | 103 | draw(#state{triangle_rot = TriangleRot, square_rot = SquareRot} = State) -> 104 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 105 | gl:loadIdentity(), 106 | gl:translatef(-1.5, 0.0, -6.0), 107 | 108 | % pyramid 109 | gl:rotatef(TriangleRot, 0.0, 1.0, 0.0), 110 | gl:'begin'(?GL_TRIANGLES), 111 | 112 | % front 113 | gl:color3f( 1.0, 0.0, 0.0), 114 | gl:vertex3f( 0.0, 1.0, 0.0), 115 | gl:color3f( 0.0, 1.0, 0.0), 116 | gl:vertex3f(-1.0, -1.0, 1.0), 117 | gl:color3f( 0.0, 0.0, 1.0), 118 | gl:vertex3f( 1.0, -1.0, 1.0), 119 | 120 | % right 121 | gl:color3f( 1.0, 0.0, 0.0), 122 | gl:vertex3f( 0.0, 1.0, 0.0), 123 | gl:color3f( 0.0, 1.0, 0.0), 124 | gl:vertex3f( 1.0, -1.0, 1.0), 125 | gl:color3f( 0.0, 0.0, 1.0), 126 | gl:vertex3f( 1.0, -1.0, -1.0), 127 | 128 | % back 129 | gl:color3f( 1.0, 0.0, 0.0), 130 | gl:vertex3f( 0.0, 1.0, 0.0), 131 | gl:color3f( 0.0, 1.0, 0.0), 132 | gl:vertex3f( 1.0, -1.0, -1.0), 133 | gl:color3f( 0.0, 0.0, 1.0), 134 | gl:vertex3f(-1.0, -1.0, -1.0), 135 | 136 | % left 137 | gl:color3f( 1.0, 0.0, 0.0), 138 | gl:vertex3f( 0.0, 1.0, 0.0), 139 | gl:color3f( 0.0, 1.0, 0.0), 140 | gl:vertex3f(-1.0, -1.0, -1.0), 141 | gl:color3f( 0.0, 0.0, 1.0), 142 | gl:vertex3f(-1.0, -1.0, 1.0), 143 | gl:'end'(), 144 | 145 | gl:loadIdentity(), 146 | gl:translatef(1.5, 0.0, -7.0), 147 | 148 | % cube 149 | gl:rotatef(SquareRot, 1.0, 1.0, 1.0), 150 | gl:'begin'(?GL_QUADS), 151 | 152 | % top 153 | gl:color3f(0.0, 1.0, 0.0), 154 | gl:vertex3f( 1.0, 1.0, -1.0), 155 | gl:vertex3f(-1.0, 1.0, -1.0), 156 | gl:vertex3f(-1.0, 1.0, 1.0), 157 | gl:vertex3f( 1.0, 1.0, 1.0), 158 | 159 | % bottom 160 | gl:color3f(1.0, 0.5, 0.0), 161 | gl:vertex3f( 1.0, -1.0, 1.0), 162 | gl:vertex3f(-1.0, -1.0, 1.0), 163 | gl:vertex3f(-1.0, -1.0, -1.0), 164 | gl:vertex3f( 1.0, -1.0, -1.0), 165 | 166 | % front 167 | gl:color3f(1.0, 0.0, 0.0), 168 | gl:vertex3f( 1.0, 1.0, 1.0), 169 | gl:vertex3f(-1.0, 1.0, 1.0), 170 | gl:vertex3f(-1.0, -1.0, 1.0), 171 | gl:vertex3f( 1.0, -1.0, 1.0), 172 | 173 | % back 174 | gl:color3f(1.0, 1.0, 0.0), 175 | gl:vertex3f( 1.0, -1.0, -1.0), 176 | gl:vertex3f(-1.0, -1.0, -1.0), 177 | gl:vertex3f(-1.0, 1.0, -1.0), 178 | gl:vertex3f( 1.0, 1.0, -1.0), 179 | 180 | % left 181 | gl:color3f(0.0, 0.0, 1.0), 182 | gl:vertex3f(-1.0, 1.0, 1.0), 183 | gl:vertex3f(-1.0, 1.0, -1.0), 184 | gl:vertex3f(-1.0, -1.0, -1.0), 185 | gl:vertex3f(-1.0, -1.0, 1.0), 186 | 187 | % right 188 | gl:color3f(1.0, 0.0, 1.0), 189 | gl:vertex3f( 1.0, 1.0, -1.0), 190 | gl:vertex3f( 1.0, 1.0, 1.0), 191 | gl:vertex3f( 1.0, -1.0, 1.0), 192 | gl:vertex3f( 1.0, -1.0, -1.0), 193 | gl:'end'(), 194 | 195 | State#state{triangle_rot = TriangleRot + 0.2, square_rot = SquareRot - 0.15}. 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /lesson06.erl: -------------------------------------------------------------------------------- 1 | -module(lesson06). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time, 19 | texture, 20 | xrot = 0.0, 21 | yrot = 0.0, 22 | zrot = 0.0 23 | }). 24 | 25 | -record(texture, {id, w, h, minx, miny, maxx, maxy}). 26 | 27 | start(Config) -> 28 | wx_object:start_link(?MODULE, Config, []). 29 | 30 | init(Config) -> 31 | wx:batch(fun() -> do_init(Config) end). 32 | 33 | do_init(Config) -> 34 | Parent = proplists:get_value(parent, Config), 35 | Size = proplists:get_value(size, Config), 36 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 37 | GLAttrib = [{attribList, [?WX_GL_RGBA, 38 | ?WX_GL_DOUBLEBUFFER, 39 | ?WX_GL_MIN_RED, 8, 40 | ?WX_GL_MIN_GREEN, 8, 41 | ?WX_GL_MIN_BLUE, 8, 42 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 43 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 44 | wxGLCanvas:connect(Canvas, size), 45 | wxWindow:hide(Parent), 46 | wxWindow:reparent(Canvas, Parent), 47 | wxWindow:show(Parent), 48 | wxGLCanvas:setCurrent(Canvas), 49 | 50 | State = #state{parent = Parent, config = Config, canvas = Canvas}, 51 | NewState = setup_gl(State), 52 | 53 | Timer = timer:send_interval(20, self(), update), 54 | 55 | {Parent, NewState#state{timer = Timer}}. 56 | 57 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 58 | case W =:= 0 orelse H =:= 0 of 59 | true -> skip; 60 | _ -> 61 | resize_gl_scene(W, H) 62 | end, 63 | {noreply, State}. 64 | 65 | handle_info(update, State) -> 66 | NewState = wx:batch(fun() -> render(State) end), 67 | {noreply, NewState}; 68 | 69 | handle_info(stop, State) -> 70 | timer:cancel(State#state.timer), 71 | catch wxGLCanvas:destroy(State#state.canvas), 72 | {stop, normal, State}. 73 | 74 | handle_call(Msg, _From, State) -> 75 | io:format("Call: ~p~n", [Msg]), 76 | {reply, ok, State}. 77 | 78 | code_change(_, _, State) -> 79 | {stop, not_yet_implemented, State}. 80 | 81 | terminate(_Reason, State) -> 82 | catch wxGLCanvas:destroy(State#state.canvas), 83 | timer:cancel(State#state.timer), 84 | timer:sleep(300). 85 | 86 | resize_gl_scene(Width, Height) -> 87 | gl:viewport(0, 0, Width, Height), 88 | gl:matrixMode(?GL_PROJECTION), 89 | gl:loadIdentity(), 90 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 91 | gl:matrixMode(?GL_MODELVIEW), 92 | gl:loadIdentity(). 93 | 94 | setup_gl(#state{parent = Window} = State) -> 95 | {Width, Height} = wxWindow:getClientSize(Window), 96 | resize_gl_scene(Width, Height), 97 | gl:enable(?GL_TEXTURE_2D), 98 | gl:shadeModel(?GL_SMOOTH), 99 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 100 | gl:clearDepth(1.0), 101 | gl:enable(?GL_DEPTH_TEST), 102 | gl:depthFunc(?GL_LEQUAL), 103 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 104 | Texture = load_texture_by_image(wxImage:new("crate.jpg")), 105 | State#state{texture = Texture}. 106 | 107 | render(#state{parent = _Window, canvas = Canvas} = State) -> 108 | NewState = draw(State), 109 | wxGLCanvas:swapBuffers(Canvas), 110 | NewState. 111 | 112 | draw(#state{xrot = XRot, yrot = YRot, zrot = ZRot, texture = Texture} = State) -> 113 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 114 | gl:loadIdentity(), 115 | gl:translatef(0.0, 0.0, -5.0), 116 | 117 | gl:rotatef(XRot, 1.0, 0.0, 0.0), 118 | gl:rotatef(YRot, 0.0, 1.0, 0.0), 119 | gl:rotatef(ZRot, 0.0, 0.0, 1.0), 120 | 121 | gl:bindTexture(?GL_TEXTURE_2D, Texture#texture.id), 122 | gl:'begin'(?GL_QUADS), 123 | 124 | % front face 125 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 126 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 127 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 128 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 129 | 130 | % back face 131 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 132 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 133 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 134 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 135 | 136 | % top face 137 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 138 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, 1.0, 1.0), 139 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, 1.0, 1.0), 140 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 141 | 142 | % bottom face 143 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, -1.0, -1.0), 144 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, -1.0, -1.0), 145 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 146 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 147 | 148 | % right face 149 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 150 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 151 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 152 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 153 | 154 | % left face 155 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 156 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 157 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 158 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 159 | 160 | gl:'end'(), 161 | 162 | State#state{xrot = XRot + 0.3, yrot = YRot + 0.2, zrot = ZRot + 0.4}. 163 | 164 | load_texture_by_image(Image) -> 165 | ImageWidth = wxImage:getWidth(Image), 166 | ImageHeight = wxImage:getHeight(Image), 167 | Width = get_power_of_two_roof(ImageWidth), 168 | Height = get_power_of_two_roof(ImageHeight), 169 | Data = get_image_data(Image), 170 | 171 | % Create opengl texture for the image 172 | [TextureID] = gl:genTextures(1), 173 | gl:bindTexture(?GL_TEXTURE_2D, TextureID), 174 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, ?GL_LINEAR), 175 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, ?GL_LINEAR), 176 | Format = case wxImage:hasAlpha(Image) of 177 | true -> ?GL_RGBA; 178 | false -> ?GL_RGB 179 | end, 180 | gl:texImage2D(?GL_TEXTURE_2D, 0, Format, Width, Height, 0, Format, ?GL_UNSIGNED_BYTE, Data), 181 | #texture{id = TextureID, w = ImageWidth, h = ImageHeight, 182 | minx = 0, miny = 0, maxx = ImageWidth / Width, maxy = ImageHeight / Height}. 183 | 184 | get_image_data(Image) -> 185 | RGB = wxImage:getData(Image), 186 | case wxImage:hasAlpha(Image) of 187 | true -> 188 | Alpha = wxImage:getAlpha(Image), 189 | interleave_rgb_and_alpha(RGB, Alpha); 190 | false -> 191 | RGB 192 | end. 193 | 194 | interleave_rgb_and_alpha(RGB, Alpha) -> 195 | list_to_binary( 196 | lists:zipwith(fun({R, G, B}, A) -> 197 | <> 198 | end, 199 | [{R,G,B} || <> <= RGB], 200 | [A || <> <= Alpha])). 201 | 202 | 203 | get_power_of_two_roof(X) -> 204 | get_power_of_two_roof_2(1, X). 205 | 206 | get_power_of_two_roof_2(N, X) when N >= X -> N; 207 | get_power_of_two_roof_2(N, X) -> get_power_of_two_roof_2(N*2, X). 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /lesson12.erl: -------------------------------------------------------------------------------- 1 | -module(lesson12). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time, 19 | texture, 20 | box, 21 | top, 22 | xrot = 0.0, 23 | yrot = 0.0, 24 | box_colors = [{1.0, 0.0, 0.0}, {1.0, 0.5, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0}], 25 | top_colors = [{0.5, 0.0, 0.0}, {0.5, 0.25, 0.0}, {0.5, 0.5, 0.0}, {0.0, 0.5, 0.0}, {0.0, 0.5, 0.5}] 26 | }). 27 | 28 | -record(texture, {id, w, h, minx, miny, maxx, maxy}). 29 | 30 | start(Config) -> 31 | wx_object:start_link(?MODULE, Config, []). 32 | 33 | init(Config) -> 34 | wx:batch(fun() -> do_init(Config) end). 35 | 36 | do_init(Config) -> 37 | Parent = proplists:get_value(parent, Config), 38 | Size = proplists:get_value(size, Config), 39 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 40 | GLAttrib = [{attribList, [?WX_GL_RGBA, 41 | ?WX_GL_DOUBLEBUFFER, 42 | ?WX_GL_MIN_RED, 8, 43 | ?WX_GL_MIN_GREEN, 8, 44 | ?WX_GL_MIN_BLUE, 8, 45 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 46 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 47 | wxGLCanvas:connect(Canvas, size), 48 | wxGLCanvas:connect(Canvas, key_up), 49 | wxWindow:hide(Parent), 50 | wxWindow:reparent(Canvas, Parent), 51 | wxWindow:show(Parent), 52 | wxGLCanvas:setCurrent(Canvas), 53 | 54 | State = #state{parent = Parent, config = Config, canvas = Canvas}, 55 | NewState = setup_gl(State), 56 | 57 | Timer = timer:send_interval(20, self(), update), 58 | 59 | {Parent, NewState#state{timer = Timer}}. 60 | 61 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 62 | case W =:= 0 orelse H =:= 0 of 63 | true -> skip; 64 | _ -> 65 | resize_gl_scene(W, H) 66 | end, 67 | {noreply, State}; 68 | 69 | handle_event(#wx{event = #wxKey{keyCode = KeyCode}}, State) -> 70 | NewState = case KeyCode of 71 | ?WXK_UP -> 72 | State#state{xrot = State#state.xrot - 0.2}; 73 | ?WXK_DOWN -> 74 | State#state{xrot = State#state.xrot + 0.2}; 75 | ?WXK_LEFT -> 76 | State#state{yrot = State#state.yrot - 0.2}; 77 | ?WXK_RIGHT -> 78 | State#state{yrot = State#state.yrot + 0.2}; 79 | Key -> 80 | io:format("key: ~p~n", [Key]), 81 | State 82 | end, 83 | {noreply, NewState}; 84 | 85 | handle_event(Msg, State) -> 86 | io:format("~p~n", [Msg]), 87 | {noreply, State}. 88 | 89 | handle_info(update, State) -> 90 | NewState = wx:batch(fun() -> render(State) end), 91 | {noreply, NewState}; 92 | 93 | handle_info(stop, State) -> 94 | timer:cancel(State#state.timer), 95 | catch wxGLCanvas:destroy(State#state.canvas), 96 | {stop, normal, State}. 97 | 98 | handle_call(Msg, _From, State) -> 99 | io:format("Call: ~p~n", [Msg]), 100 | {reply, ok, State}. 101 | 102 | code_change(_, _, State) -> 103 | {stop, not_yet_implemented, State}. 104 | 105 | terminate(_Reason, State) -> 106 | catch wxGLCanvas:destroy(State#state.canvas), 107 | timer:cancel(State#state.timer), 108 | timer:sleep(300). 109 | 110 | resize_gl_scene(Width, Height) -> 111 | gl:viewport(0, 0, Width, Height), 112 | gl:matrixMode(?GL_PROJECTION), 113 | gl:loadIdentity(), 114 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 115 | gl:matrixMode(?GL_MODELVIEW), 116 | gl:loadIdentity(). 117 | 118 | setup_gl(#state{parent = Window} = State) -> 119 | {Width, Height} = wxWindow:getClientSize(Window), 120 | resize_gl_scene(Width, Height), 121 | NewState = setup_display_lists(State), 122 | 123 | gl:enable(?GL_TEXTURE_2D), 124 | gl:shadeModel(?GL_SMOOTH), 125 | gl:clearColor(0.0, 0.0, 0.0, 0.5), 126 | gl:clearDepth(1.0), 127 | gl:enable(?GL_DEPTH_TEST), 128 | gl:depthFunc(?GL_LEQUAL), 129 | gl:enable(?GL_LIGHT0), 130 | gl:enable(?GL_LIGHTING), 131 | gl:enable(?GL_COLOR_MATERIAL), 132 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 133 | Texture = load_texture_by_image(wxImage:new("crate.jpg")), 134 | NewState#state{texture = Texture}. 135 | 136 | setup_display_lists(State) -> 137 | Box = gl:genLists(2), 138 | gl:newList(Box, ?GL_COMPILE), 139 | gl:'begin'(?GL_QUADS), 140 | % front face 141 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 142 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 143 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 144 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 145 | 146 | % back face 147 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 148 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 149 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 150 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 151 | 152 | % bottom face 153 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, -1.0, -1.0), 154 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, -1.0, -1.0), 155 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 156 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 157 | 158 | % right face 159 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 160 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 161 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 162 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 163 | 164 | % left face 165 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 166 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 167 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 168 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 169 | 170 | gl:'end'(), 171 | gl:endList(), 172 | 173 | Top = Box + 1, 174 | gl:newList(Top, ?GL_COMPILE), 175 | 176 | gl:'begin'(?GL_QUADS), 177 | 178 | % top face 179 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 180 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, 1.0, 1.0), 181 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, 1.0, 1.0), 182 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 183 | 184 | gl:'end'(), 185 | gl:endList(), 186 | 187 | State#state{box = Box, top = Top}. 188 | 189 | render(#state{parent = _Window, canvas = Canvas} = State) -> 190 | NewState = draw(State), 191 | wxGLCanvas:swapBuffers(Canvas), 192 | NewState. 193 | 194 | draw(#state{texture = Texture} = State) -> 195 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 196 | 197 | gl:bindTexture(?GL_TEXTURE_2D, Texture#texture.id), 198 | 199 | draw_cubes(State), 200 | 201 | State. 202 | 203 | draw_cubes(State) -> 204 | draw_cubes(0, 1, State). 205 | draw_cubes(_XLoop, 6, State) -> 206 | State; 207 | draw_cubes(Loop, Loop, State) -> 208 | draw_cubes(0, Loop+1, State); 209 | draw_cubes(XLoop, YLoop, #state{box = Box, top = Top, xrot = XRot, yrot = YRot, box_colors = BoxColors, top_colors = TopColors} = State) -> 210 | gl:loadIdentity(), 211 | gl:translatef(1.4 + (XLoop * 2.8) - (YLoop * 1.4), ((6.0 - YLoop) * 2.4) - 7.0, -20.0), 212 | gl:rotatef(45.0 - (2.0 * YLoop) + XRot, 1.0, 0.0, 0.0), 213 | gl:rotatef(45.0 + YRot, 0.0, 1.0, 0.0), 214 | gl:color3fv(lists:nth(YLoop, BoxColors)), 215 | gl:callList(Box), 216 | gl:color3fv(lists:nth(YLoop, TopColors)), 217 | gl:callList(Top), 218 | draw_cubes(XLoop + 1, YLoop, State). 219 | 220 | 221 | load_texture_by_image(Image) -> 222 | ImageWidth = wxImage:getWidth(Image), 223 | ImageHeight = wxImage:getHeight(Image), 224 | Width = get_power_of_two_roof(ImageWidth), 225 | Height = get_power_of_two_roof(ImageHeight), 226 | Data = get_image_data(Image), 227 | 228 | % Create opengl texture for the image 229 | [TextureID] = gl:genTextures(1), 230 | gl:bindTexture(?GL_TEXTURE_2D, TextureID), 231 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, ?GL_LINEAR), 232 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, ?GL_LINEAR), 233 | Format = case wxImage:hasAlpha(Image) of 234 | true -> ?GL_RGBA; 235 | false -> ?GL_RGB 236 | end, 237 | gl:texImage2D(?GL_TEXTURE_2D, 0, Format, Width, Height, 0, Format, ?GL_UNSIGNED_BYTE, Data), 238 | #texture{id = TextureID, w = ImageWidth, h = ImageHeight, 239 | minx = 0, miny = 0, maxx = ImageWidth / Width, maxy = ImageHeight / Height}. 240 | 241 | get_image_data(Image) -> 242 | RGB = wxImage:getData(Image), 243 | case wxImage:hasAlpha(Image) of 244 | true -> 245 | Alpha = wxImage:getAlpha(Image), 246 | interleave_rgb_and_alpha(RGB, Alpha); 247 | false -> 248 | RGB 249 | end. 250 | 251 | interleave_rgb_and_alpha(RGB, Alpha) -> 252 | list_to_binary( 253 | lists:zipwith(fun({R, G, B}, A) -> 254 | <> 255 | end, 256 | [{R,G,B} || <> <= RGB], 257 | [A || <> <= Alpha])). 258 | 259 | 260 | get_power_of_two_roof(X) -> 261 | get_power_of_two_roof_2(1, X). 262 | 263 | get_power_of_two_roof_2(N, X) when N >= X -> N; 264 | get_power_of_two_roof_2(N, X) -> get_power_of_two_roof_2(N*2, X). 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /lesson07.erl: -------------------------------------------------------------------------------- 1 | -module(lesson07). 2 | 3 | -behaviour(wx_object). 4 | 5 | -export([init/1, code_change/3, handle_info/2, handle_event/2, 6 | handle_call/3, terminate/2, 7 | start/1]). 8 | 9 | -include_lib("wx/include/wx.hrl"). 10 | -include_lib("wx/include/gl.hrl"). 11 | -include_lib("wx/include/glu.hrl"). 12 | 13 | -record(state, { 14 | parent, 15 | config, 16 | canvas, 17 | timer, 18 | time, 19 | texture_near, 20 | texture_linear, 21 | texture_mipmap, 22 | xrot = 0.0, 23 | yrot = 0.0, 24 | xspeed = 0.15, 25 | yspeed = 0.15, 26 | light_ambient = {0.5, 0.5, 0.5, 0.5}, 27 | light_diffuse = {1.0, 1.0, 1.0, 1.0}, 28 | light_position = {0.0, 0.0, 2.0, 1.0}, 29 | zindex = -5.0, 30 | lights = false 31 | }). 32 | 33 | -record(texture, {id, w, h, minx, miny, maxx, maxy}). 34 | 35 | start(Config) -> 36 | wx_object:start_link(?MODULE, Config, []). 37 | 38 | init(Config) -> 39 | wx:batch(fun() -> do_init(Config) end). 40 | 41 | do_init(Config) -> 42 | Parent = proplists:get_value(parent, Config), 43 | Size = proplists:get_value(size, Config), 44 | Opts = [{size, Size}, {style, ?wxSUNKEN_BORDER}], 45 | GLAttrib = [{attribList, [?WX_GL_RGBA, 46 | ?WX_GL_DOUBLEBUFFER, 47 | ?WX_GL_MIN_RED, 8, 48 | ?WX_GL_MIN_GREEN, 8, 49 | ?WX_GL_MIN_BLUE, 8, 50 | ?WX_GL_DEPTH_SIZE, 24, 0]}], 51 | Canvas = wxGLCanvas:new(Parent, Opts ++ GLAttrib), 52 | wxGLCanvas:connect(Canvas, size), 53 | wxGLCanvas:connect(Canvas, key_up), 54 | wxWindow:hide(Parent), 55 | wxWindow:reparent(Canvas, Parent), 56 | wxWindow:show(Parent), 57 | wxGLCanvas:setCurrent(Canvas), 58 | 59 | State = #state{parent = Parent, config = Config, canvas = Canvas}, 60 | NewState = setup_gl(State), 61 | 62 | Timer = timer:send_interval(20, self(), update), 63 | 64 | {Parent, NewState#state{timer = Timer}}. 65 | 66 | handle_event(#wx{event = #wxSize{size = {W, H}}}, State) -> 67 | case W =:= 0 orelse H =:= 0 of 68 | true -> skip; 69 | _ -> 70 | resize_gl_scene(W, H) 71 | end, 72 | {noreply, State}; 73 | 74 | handle_event(#wx{event = #wxKey{keyCode = KeyCode}}, State) -> 75 | NewState = case KeyCode of 76 | $L -> 77 | case State#state.lights of 78 | true -> 79 | gl:disable(?GL_LIGHTING), 80 | State#state{lights = false}; 81 | false -> 82 | gl:enable(?GL_LIGHTING), 83 | State#state{lights = true} 84 | end; 85 | $I -> 86 | State#state{zindex = State#state.zindex + 0.02}; 87 | $O -> 88 | State#state{zindex = State#state.zindex - 0.02}; 89 | ?WXK_UP -> 90 | State#state{xspeed = State#state.xspeed + 0.01}; 91 | ?WXK_DOWN -> 92 | State#state{xspeed = State#state.xspeed - 0.01}; 93 | ?WXK_LEFT -> 94 | State#state{yspeed = State#state.yspeed + 0.01}; 95 | ?WXK_RIGHT -> 96 | State#state{yspeed = State#state.yspeed - 0.01}; 97 | _ -> 98 | State 99 | end, 100 | {noreply, NewState}; 101 | 102 | handle_event(Msg, State) -> 103 | io:format("~p~n", [Msg]), 104 | {noreply, State}. 105 | 106 | handle_info(update, State) -> 107 | NewState = wx:batch(fun() -> render(State) end), 108 | {noreply, NewState}; 109 | 110 | handle_info(stop, State) -> 111 | timer:cancel(State#state.timer), 112 | catch wxGLCanvas:destroy(State#state.canvas), 113 | {stop, normal, State}. 114 | 115 | handle_call(Msg, _From, State) -> 116 | io:format("Call: ~p~n", [Msg]), 117 | {reply, ok, State}. 118 | 119 | code_change(_, _, State) -> 120 | {stop, not_yet_implemented, State}. 121 | 122 | terminate(_Reason, State) -> 123 | catch wxGLCanvas:destroy(State#state.canvas), 124 | timer:cancel(State#state.timer), 125 | timer:sleep(300). 126 | 127 | resize_gl_scene(Width, Height) -> 128 | gl:viewport(0, 0, Width, Height), 129 | gl:matrixMode(?GL_PROJECTION), 130 | gl:loadIdentity(), 131 | glu:perspective(45.0, Width / Height, 0.1, 100.0), 132 | gl:matrixMode(?GL_MODELVIEW), 133 | gl:loadIdentity(). 134 | 135 | setup_gl(#state{parent = Window} = State) -> 136 | {Width, Height} = wxWindow:getClientSize(Window), 137 | resize_gl_scene(Width, Height), 138 | gl:enable(?GL_TEXTURE_2D), 139 | gl:shadeModel(?GL_SMOOTH), 140 | gl:clearColor(0.0, 0.0, 0.0, 0.0), 141 | gl:clearDepth(1.0), 142 | gl:enable(?GL_DEPTH_TEST), 143 | gl:depthFunc(?GL_LEQUAL), 144 | gl:hint(?GL_PERSPECTIVE_CORRECTION_HINT, ?GL_NICEST), 145 | 146 | gl:lightfv(?GL_LIGHT1, ?GL_AMBIENT, State#state.light_ambient), 147 | gl:lightfv(?GL_LIGHT1, ?GL_DIFFUSE, State#state.light_diffuse), 148 | gl:lightfv(?GL_LIGHT1, ?GL_POSITION, State#state.light_position), 149 | gl:enable(?GL_LIGHT1), 150 | 151 | Image = wxImage:new("crate.jpg"), 152 | TextureNear = load_texture_by_image(Image, ?GL_NEAREST, ?GL_NEAREST), 153 | TextureLinear = load_texture_by_image(Image, ?GL_LINEAR, ?GL_LINEAR), 154 | TextureMipMap = load_texture_by_image(Image, ?GL_LINEAR, ?GL_LINEAR_MIPMAP_NEAREST), 155 | build_mipmap_by_image(Image), 156 | 157 | State#state{texture_near = TextureNear, texture_linear = TextureLinear, texture_mipmap = TextureMipMap}. 158 | 159 | render(#state{parent = _Window, canvas = Canvas} = State) -> 160 | NewState = draw(State), 161 | wxGLCanvas:swapBuffers(Canvas), 162 | NewState. 163 | 164 | draw(#state{xrot = XRot, yrot = YRot} = State) -> 165 | gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 166 | gl:loadIdentity(), 167 | gl:translatef(0.0, 0.0, State#state.zindex), 168 | 169 | gl:rotatef(XRot, 1.0, 0.0, 0.0), 170 | gl:rotatef(YRot, 0.0, 1.0, 0.0), 171 | 172 | Texture = State#state.texture_linear, 173 | gl:bindTexture(?GL_TEXTURE_2D, Texture#texture.id), 174 | gl:'begin'(?GL_QUADS), 175 | 176 | % front face 177 | gl:normal3f(0.0, 0.0, 1.0), 178 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 179 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 180 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 181 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 182 | 183 | % back face 184 | gl:normal3f(0.0, 0.0, -1.0), 185 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 186 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 187 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 188 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 189 | 190 | % top face 191 | gl:normal3f(0.0, 1.0, 0.0), 192 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 193 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, 1.0, 1.0), 194 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, 1.0, 1.0), 195 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 196 | 197 | % bottom face 198 | gl:normal3f(0.0, -1.0, 0.0), 199 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, -1.0, -1.0), 200 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, -1.0, -1.0), 201 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 202 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 203 | 204 | % right face 205 | gl:normal3f(1.0, 0.0, 0.0), 206 | gl:texCoord2f(1.0, 0.0), gl:vertex3f( 1.0, -1.0, -1.0), 207 | gl:texCoord2f(1.0, 1.0), gl:vertex3f( 1.0, 1.0, -1.0), 208 | gl:texCoord2f(0.0, 1.0), gl:vertex3f( 1.0, 1.0, 1.0), 209 | gl:texCoord2f(0.0, 0.0), gl:vertex3f( 1.0, -1.0, 1.0), 210 | 211 | % left face 212 | gl:normal3f(-1.0, 0.0, 0.0), 213 | gl:texCoord2f(0.0, 0.0), gl:vertex3f(-1.0, -1.0, -1.0), 214 | gl:texCoord2f(1.0, 0.0), gl:vertex3f(-1.0, -1.0, 1.0), 215 | gl:texCoord2f(1.0, 1.0), gl:vertex3f(-1.0, 1.0, 1.0), 216 | gl:texCoord2f(0.0, 1.0), gl:vertex3f(-1.0, 1.0, -1.0), 217 | 218 | gl:'end'(), 219 | 220 | State#state{xrot = XRot + State#state.xspeed, yrot = YRot + State#state.yspeed}. 221 | 222 | load_texture_by_image(Image, FilterMag, FilterMin) -> 223 | ImageWidth = wxImage:getWidth(Image), 224 | ImageHeight = wxImage:getHeight(Image), 225 | Width = get_power_of_two_roof(ImageWidth), 226 | Height = get_power_of_two_roof(ImageHeight), 227 | Data = get_image_data(Image), 228 | 229 | % Create opengl texture for the image 230 | [TextureID] = gl:genTextures(1), 231 | gl:bindTexture(?GL_TEXTURE_2D, TextureID), 232 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, FilterMag), 233 | gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, FilterMin), 234 | Format = case wxImage:hasAlpha(Image) of 235 | true -> ?GL_RGBA; 236 | false -> ?GL_RGB 237 | end, 238 | gl:texImage2D(?GL_TEXTURE_2D, 0, Format, Width, Height, 0, Format, ?GL_UNSIGNED_BYTE, Data), 239 | #texture{id = TextureID, w = ImageWidth, h = ImageHeight, 240 | minx = 0, miny = 0, maxx = ImageWidth / Width, maxy = ImageHeight / Height}. 241 | 242 | build_mipmap_by_image(Image) -> 243 | ImageWidth = wxImage:getWidth(Image), 244 | ImageHeight = wxImage:getHeight(Image), 245 | Width = get_power_of_two_roof(ImageWidth), 246 | Height = get_power_of_two_roof(ImageHeight), 247 | Data = get_image_data(Image), 248 | 249 | % Create opengl texture for the image 250 | Format = case wxImage:hasAlpha(Image) of 251 | true -> ?GL_RGBA; 252 | false -> ?GL_RGB 253 | end, 254 | glu:build2DMipmaps(?GL_TEXTURE_2D, Format, Width, Height, Format, ?GL_UNSIGNED_BYTE, Data). 255 | 256 | get_image_data(Image) -> 257 | RGB = wxImage:getData(Image), 258 | case wxImage:hasAlpha(Image) of 259 | true -> 260 | Alpha = wxImage:getAlpha(Image), 261 | interleave_rgb_and_alpha(RGB, Alpha); 262 | false -> 263 | RGB 264 | end. 265 | 266 | interleave_rgb_and_alpha(RGB, Alpha) -> 267 | list_to_binary( 268 | lists:zipwith(fun({R, G, B}, A) -> 269 | <> 270 | end, 271 | [{R,G,B} || <> <= RGB], 272 | [A || <> <= Alpha])). 273 | 274 | 275 | get_power_of_two_roof(X) -> 276 | get_power_of_two_roof_2(1, X). 277 | 278 | get_power_of_two_roof_2(N, X) when N >= X -> N; 279 | get_power_of_two_roof_2(N, X) -> get_power_of_two_roof_2(N*2, X). 280 | 281 | 282 | 283 | 284 | 285 | 286 | --------------------------------------------------------------------------------