1 module armos.app.glfwwindow;
2 
3 import armos.app;
4 import armos.events;
5 import derelict.opengl3.gl;
6 import armos.app.window;
7 import armos.math;
8 import armos.graphics.renderer;
9 /++
10     GLFWを利用したWindowです.armosではデフォルトでこのclassを元にWindowが生成されます.
11 +/
12 class GLFWWindow : Window{
13     import derelict.glfw3.glfw3;
14     import std.range:put;
15     mixin BaseWindow;
16 
17     public{
18         enum bool hasRenderer = true;
19         /++
20             Params:
21             apprication = Windowとひも付けされるアプリケーションです.
22         +/
23         this(WindowConfig config = new WindowConfig, GLFWwindow* sharedContext = null){
24             DerelictGL.load();
25             DerelictGLFW3.load();
26 
27             if( !glfwInit() ){}
28 
29             glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, config.glVersionMajor);
30             glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, config.glVersionMinor);
31             
32             import armos.utils.semver;
33             if(config.glVersion >= SemVer("3.2.0")){
34                 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
35                 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
36             }
37 
38             _window = glfwCreateWindow(config.width, config.height, cast(char*)_name, null, sharedContext);
39             if(!_window){close;}
40 
41             GLFWWindow.glfwWindowToArmosGLFWWindow[_window] = this;
42 
43             glfwMakeContextCurrent(_window);
44 
45             if(config.glVersion >= SemVer("3.2.0")){
46                 DerelictGL3.reload();
47             }else{
48                 DerelictGL.reload();
49             }
50 
51             initGLFWEvents();
52             _subjects = new CoreSubjects;
53 
54             glfwSwapInterval(0);
55             glfwSwapBuffers(_window);
56             
57             writeVersion;
58 
59 
60             _renderer = new Renderer;
61         }
62 
63         ~this(){
64             GLFWWindow.glfwWindowToArmosGLFWWindow.remove(_window);
65         }
66 
67         void size(Vector2i size){
68             glfwSetWindowSize(_window, size[0], size[1]);
69         }
70 
71         /++
72             Windowのサイズを返します.
73         +/
74         Vector2i size()const{
75             auto vec = Vector2i();
76             glfwGetWindowSize(cast(GLFWwindow*)(_window), &vec[0], &vec[1]);
77             return vec;
78         }
79 
80         /++
81             Windowのframw bufferのサイズを返します.
82         +/
83         Vector2i frameBufferSize()const{
84             auto vec = Vector2i();
85             glfwGetFramebufferSize(cast(GLFWwindow*)(_window), &vec[0], &vec[1]);
86             return vec;
87         }
88 
89         /++
90             イベントが発生している場合,登録されたイベントを実行します
91         +/
92         void pollEvents(){
93             glfwPollEvents();
94         }
95 
96         void setup(){
97             _renderer.setup();
98             put(_subjects.setup, SetupEvent());
99         }
100 
101         /++
102             Windowを更新します.
103         +/
104         void update(){
105             put(_subjects.update, UpdateEvent());
106             _shouldClose = cast(bool)glfwWindowShouldClose(_window);
107         }
108 
109         void draw(){
110             if(_renderer){
111                 _renderer.startRender();
112             }
113             put(_subjects.draw, DrawEvent());
114             if(_renderer){
115                 _renderer.finishRender();
116             }
117             glfwSwapBuffers(_window);
118         }
119 
120         /++
121             Windowを閉じます.
122         +/
123         void close(){
124             put(_subjects.exit, ExitEvent());
125             _shouldClose = true;
126             glfwDestroyWindow(_window);
127         }
128 
129         void name(in string str){
130             import std..string;
131             _name = str;
132             glfwSetWindowTitle(_window, str.toStringz);
133         }
134         
135         void select(){
136             glfwMakeContextCurrent(_window);
137         };
138 
139         /// VerticalSync
140         void verticalSync(in bool f){
141             glfwSwapInterval(f);
142         };
143 
144         ///
145         float pixelScreenCoordScale()const{
146             return frameBufferSize.x / size.x;
147         };
148 
149         void initEvents(Application app){
150             assert(_subjects);
151             import rx;
152             _subjects.setup.doSubscribe!(event => app.setup(event));
153             _subjects.update.doSubscribe!(event => app.update(event));
154             _subjects.draw.doSubscribe!(event => app.draw(event));
155             _subjects.exit.doSubscribe!(event => app.exit(event));
156             _subjects.windowResize.doSubscribe!(event => app.windowResized(event));
157             _subjects.keyPressed.doSubscribe!(event => app.keyPressed(event));
158             _subjects.keyReleased.doSubscribe!(event => app.keyReleased(event));
159             _subjects.unicodeInputted.doSubscribe!(event => app.unicodeInputted(event));
160             _subjects.mouseMoved.doSubscribe!(event => app.mouseMoved(event));
161             _subjects.mousePressed.doSubscribe!(event => app.mousePressed(event));
162             _subjects.mouseDragged.doSubscribe!(event => app.mouseDragged(event));
163             _subjects.mouseReleased.doSubscribe!(event => app.mouseReleased(event));
164             _subjects.mouseScrolled.doSubscribe!(event => app.mouseScrolled(event));
165         }
166 
167         CoreObservables observables(){
168             import std.conv:to;
169             return _subjects.to!CoreObservables;
170         }
171 
172         Renderer renderer(){return _renderer;}
173 
174         GLFWwindow* context(){
175             return _window;
176         }
177     }//public
178 
179     private{
180         GLFWwindow* _window;
181         CoreSubjects _subjects;
182         Renderer _renderer;
183 
184         static extern(C) void keyCallbackFunction(GLFWwindow* window, int key, int scancode, int action, int mods){
185             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
186             mainLoop.select(currentGLFWWindow);
187             import std.conv;
188             import armos.utils.keytype;
189             if(action == GLFW_PRESS){
190                 put(currentGLFWWindow._subjects.keyPressed, KeyPressedEvent(key.to!KeyType));
191             }else if(action == GLFW_RELEASE){
192                 put(currentGLFWWindow._subjects.keyReleased, KeyReleasedEvent(key.to!KeyType));
193             }
194         }
195         
196         static extern(C) void charCallbackFunction(GLFWwindow* window, uint key){
197             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
198             mainLoop.select(currentGLFWWindow);
199             put(currentGLFWWindow._subjects.unicodeInputted, UnicodeInputtedEvent(key));
200         }
201 
202         static extern(C) void cursorPositionFunction(GLFWwindow* window, double xpos, double ypos){
203             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
204             mainLoop.select(currentGLFWWindow);
205             put(currentGLFWWindow._subjects.mouseMoved, MouseMovedEvent(cast(int)xpos, cast(int)ypos, 0));
206         }
207 
208         static extern(C ) void mouseButtonFunction(GLFWwindow* window, int button, int action, int mods){
209             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
210             mainLoop.select(currentGLFWWindow);
211 
212             double xpos, ypos;
213             glfwGetCursorPos(window, &xpos, &ypos);
214 
215             if(action == GLFW_PRESS){
216                 put(currentGLFWWindow._subjects.mousePressed, MousePressedEvent(cast(int)xpos, cast(int)ypos, button));
217             }else if(action == GLFW_RELEASE){
218                 put(currentGLFWWindow._subjects.mouseReleased, MouseReleasedEvent(cast(int)xpos, cast(int)ypos, button));
219             }
220         }
221 
222         static extern(C ) void resizeWindowFunction(GLFWwindow* window, int width, int height){
223             import armos.graphics;
224             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
225             mainLoop.select(currentGLFWWindow);
226             currentRenderer.resize();
227             put(currentGLFWWindow._subjects.windowResize, WindowResizeEvent(cast(int)width, cast(int)height));
228         }
229 
230         static extern(C ) void mouseScrolledCallback(GLFWwindow* window, double xOffset, double yOffset){
231             import armos.graphics;
232             auto currentGLFWWindow = GLFWWindow.glfwWindowToArmosGLFWWindow[window];
233             mainLoop.select(currentGLFWWindow);
234             put(currentGLFWWindow._subjects.mouseScrolled, MouseScrolledEvent(cast(int)xOffset, cast(int)yOffset));
235         }
236         
237         void writeVersion(){
238             import std.stdio, std.conv;
239             writefln("Vendor:   %s",   to!string(glGetString(GL_VENDOR)));
240             writefln("Renderer: %s",   to!string(glGetString(GL_RENDERER)));
241             writefln("Version:  %s",   to!string(glGetString(GL_VERSION)));
242             writefln("GLSL:     %s\n", to!string(glGetString(GL_SHADING_LANGUAGE_VERSION)));
243         };
244         
245 
246 
247         void initGLFWEvents(){
248             // glfwSetKeyCallback(window, &keyCallbackFunction);
249             glfwSetKeyCallback(_window, cast(GLFWkeyfun)&keyCallbackFunction);
250             glfwSetCharCallback(_window, cast(GLFWcharfun)&charCallbackFunction);
251             glfwSetCursorPosCallback(_window, cast(GLFWcursorposfun)&cursorPositionFunction);
252             glfwSetMouseButtonCallback(_window, cast(GLFWmousebuttonfun)&mouseButtonFunction);
253             glfwSetWindowSizeCallback(_window, cast(GLFWwindowsizefun)&resizeWindowFunction);
254             glfwSetScrollCallback(_window, cast(GLFWscrollfun)&mouseScrolledCallback);
255         }
256 
257         static GLFWWindow[GLFWwindow*] glfwWindowToArmosGLFWWindow;
258     }//private
259 }
260