ゲームグラフィックス特論
Window.h
[詳解]
1 #pragma once
2 
3 /*
4 ** ゲームグラフィックス特論の宿題用補助プログラム GLFW3 版
5 **
6 
7 Copyright (c) 2011-2021 Kohe Tokoi. All Rights Reserved.
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies or substantial portions of the Software.
14 
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 KOHE TOKOI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 **
26 */
27 
35 // Oculus Rift を使うなら
36 //#define USE_OCULUS_RIFT
37 
38 // Dear ImGui を使うなら
39 #define USE_IMGUI
40 
41 // 使用するマウスのボタン数
42 constexpr int BUTTON_COUNT(3);
43 
44 // 使用するユーザインタフェースの数
45 constexpr int INTERFACE_COUNT(3);
46 
47 // 補助プログラム
48 #include "gg.h"
49 using namespace gg;
50 
51 // Oculus Rift SDK ライブラリ (LibOVR) の組み込み
52 #ifdef USE_OCULUS_RIFT
53 # ifdef _MSC_VER
54 # define GLFW_EXPOSE_NATIVE_WIN32
55 # define GLFW_EXPOSE_NATIVE_WGL
56 # include <GLFW/glfw3native.h>
57 # define OVR_OS_WIN32
58 # undef APIENTRY
59 # pragma comment(lib, "LibOVR.lib")
60 # endif
61 # include <OVR_CAPI_GL.h>
62 # include <Extras/OVR_Math.h>
63 # if OVR_PRODUCT_VERSION > 0
64 # include <dxgi.h> // GetDefaultAdapterLuid のため
65 # pragma comment(lib, "dxgi.lib")
66 # endif
67 #endif
68 
69 // ImGui の組み込み
70 #ifdef USE_IMGUI
71 # include "imgui.h"
72 # include "imgui_impl_glfw.h"
73 # include "imgui_impl_opengl3.h"
74 #endif
75 
76 // 標準ライブラリ
77 #include <cmath>
78 #include <cstdlib>
79 #include <cassert>
80 #include <stdexcept>
81 #include <iostream>
82 
88 class Window
89 {
90  // ウィンドウの識別子
91  GLFWwindow *window;
92 
93  // ビューポートの横幅と高さ
94  std::array<GLsizei, 2> size;
95 
96  // フレームバッファの横幅と高さ
97  std::array<GLsizei, 2> fboSize;
98 
99  // ビューポートのアスペクト比
100  GLfloat aspect;
101 
102  // マウスの移動速度[X/Y]
103  std::array<GLfloat, 2> velocity;
104 
105  // マウスボタンの状態
106  std::array<bool, BUTTON_COUNT> status;
107 
108  // ユーザインタフェースのデータ構造
109  struct UserInterface
110  {
111  // 矢印キー
112  std::array<std::array<int, 2>, 4> arrow;
113 
114  // マウスの現在位置
115  std::array<GLfloat, 2> mouse;
116 
117  // マウスホイールの回転量
118  std::array<GLfloat, 2> wheel;
119 
120  // 現在位置[ボタン][直前/更新][X/Y]
121  std::array<std::array<std::array<GLfloat, 2>, 2>, BUTTON_COUNT> location;
122 
123  // 平行移動量[ボタン][直前/更新][X/Y/Z]
124  std::array<std::array<std::array<GLfloat, 3>, 2>, BUTTON_COUNT> translation;
125 
126  // トラックボール
127  std::array<GgTrackball, BUTTON_COUNT> trackball;
128 
129  // コンストラクタ
130  UserInterface()
131  : arrow{}
132  , mouse{}
133  , wheel{}
134  , location{}
135  , translation{}
136  {}
137 
138  // トラックボール処理を考慮した平行移動量を計算する (X, Y のみ, Z は wheel() で計算する)
139  void calcTranslation(int button, const std::array<GLfloat, 2>& velocity)
140  {
141  // マウスの相対変位
142  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
143  const GLfloat dx((mouse[0] - trackball[button].getStart(0)) * trackball[button].getScale(0));
144  const GLfloat dy((trackball[button].getStart(1) - mouse[1]) * trackball[button].getScale(1));
145 
146  // 現在位置
147  auto& l(location[button]);
148 
149  // 現在位置の更新
150  l[1][0] = dx * velocity[0] + l[0][0];
151  l[1][1] = dy * velocity[1] + l[0][1];
152 
153  // 平行移動量
154  auto& t(translation[button]);
155 
156  // 移動前の平行移動量の z 値
157  const GLfloat d(fabs(t[1][2]));
158 
159  // 平行移動量の更新
160  t[1][0] = dx * d + t[0][0];
161  t[1][1] = dy * d + t[0][1];
162 
163  // トラックボールの更新
164  trackball[button].motion(mouse[0], mouse[1]);
165  }
166  };
167 
168  // ユーザインタフェースのデータ
169  std::array<UserInterface, INTERFACE_COUNT> ui_data;
170 
171  // ユーザインタフェースの番号
172  int ui_no;
173 
174 #ifdef USE_OCULUS_RIFT
175  //
176  // Oculus Rift
177  //
178 
179  // Oculus Rift のセッション
180  ovrSession session;
181 
182  // Oculus Rift の状態
183  ovrHmdDesc hmdDesc;
184 
185  // Oculus Rift のスクリーンのサイズ
186  GLfloat screen[ovrEye_Count][4];
187 
188  // Oculus Rift 表示用の FBO
189  GLuint oculusFbo[ovrEye_Count];
190 
191  // ミラー表示用の FBO
192  GLuint mirrorFbo;
193 
194 # if OVR_PRODUCT_VERSION > 0
195 
196  // Oculus Rift に送る描画データ
197  ovrLayerEyeFov layerData;
198 
199  // Oculus Rift にレンダリングするフレームの番号
200  long long frameIndex;
201 
202  // Oculus Rift 表示用の FBO のデプステクスチャ
203  GLuint oculusDepth[ovrEye_Count];
204 
205  // ミラー表示用の FBO のサイズ
206  int mirrorWidth, mirrorHeight;
207 
208  // ミラー表示用の FBO のカラーテクスチャ
209  ovrMirrorTexture mirrorTexture;
210 
211  // グラフィックスカードのデフォルトの LUID を得る
212  inline ovrGraphicsLuid GetDefaultAdapterLuid()
213  {
214  ovrGraphicsLuid luid = ovrGraphicsLuid();
215 
216 # ifdef _MSC_VER
217  IDXGIFactory *factory(nullptr);
218 
219  if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&factory))))
220  {
221  IDXGIAdapter *adapter(nullptr);
222 
223  if (SUCCEEDED(factory->EnumAdapters(0, &adapter)))
224  {
225  DXGI_ADAPTER_DESC desc;
226 
227  adapter->GetDesc(&desc);
228  memcpy(&luid, &desc.AdapterLuid, sizeof luid);
229  adapter->Release();
230  }
231 
232  factory->Release();
233  }
234 # endif
235 
236  return luid;
237  }
238 
239  // グラフィックスカードの LUID の比較
240  inline int Compare(const ovrGraphicsLuid &lhs, const ovrGraphicsLuid &rhs)
241  {
242  return memcmp(&lhs, &rhs, sizeof(ovrGraphicsLuid));
243  }
244 
245 # else
246 
247  // Oculus Rift に送る描画データ
248  ovrLayer_Union layerData;
249 
250  // Oculus Rift のレンダリング情報
251  ovrEyeRenderDesc eyeRenderDesc[ovrEye_Count];
252 
253  // Oculus Rift の視点情報
254  ovrPosef eyePose[ovrEye_Count];
255 
256  // ミラー表示用の FBO のカラーテクスチャ
257  ovrGLTexture *mirrorTexture;
258 
259 # endif
260 #endif
261 
262  //
263  // ユーザー定義のコールバック関数へのポインタ
264  //
265  void *userPointer;
266  void (*resizeFunc)(const Window *window, int width, int height);
267  void (*keyboardFunc)(const Window *window, int key, int scancode, int action, int mods);
268  void (*mouseFunc)(const Window *window, int button, int action, int mods);
269  void (*wheelFunc)(const Window *window, double x, double y);
270 
271  //
272  // ウィンドウのサイズ変更時の処理
273  //
274  static void resize(GLFWwindow *window, int width, int height)
275  {
276  // このインスタンスの this ポインタを得る
277  Window *const instance(static_cast<Window *>(glfwGetWindowUserPointer(window)));
278 
279  if (instance)
280  {
281  // ウィンドウのサイズを保存する
282  instance->size[0] = width;
283  instance->size[1] = height;
284 
285  // トラックボール処理の範囲を設定する
286  for (auto& current_if : instance->ui_data)
287  {
288  for (auto& t : current_if.trackball)
289  {
290  t.region(width, height);
291  }
292  }
293 
294 #ifndef USE_OCULUS_RIFT
295  // ウィンドウのアスペクト比を保存する
296  instance->aspect = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
297 
298  // ウィンドウ全体に描画する
299  glfwGetFramebufferSize(window, &instance->fboSize[0], &instance->fboSize[1]);
300  glViewport(0, 0, instance->fboSize[0], instance->fboSize[1]);
301 #endif
302 
303  // ユーザー定義のコールバック関数の呼び出し
304  if (instance->resizeFunc) (*instance->resizeFunc)(instance, width, height);
305  }
306  }
307 
308  //
309  // キーボードをタイプした時の処理
310  //
311  static void keyboard(GLFWwindow *window, int key, int scancode, int action, int mods)
312  {
313 #ifdef USE_IMGUI
314  // ImGui のウィンドウが選択されていたらキーボードの処理を行わない
315  if (ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)) return;
316 #endif
317 
318  // このインスタンスの this ポインタを得る
319  Window *const instance(static_cast<Window *>(glfwGetWindowUserPointer(window)));
320 
321  if (instance && action)
322  {
323  // ユーザー定義のコールバック関数の呼び出し
324  if (instance->keyboardFunc) (*instance->keyboardFunc)(instance, key, scancode, action, mods);
325 
326  // 対象のユーザインタフェース
327  auto& current_if(instance->ui_data[instance->ui_no]);
328 
329  switch (key)
330  {
331  case GLFW_KEY_HOME:
332 
333  // トラックボールをリセットする
334  instance->reset_trackball();
335 
336  case GLFW_KEY_END:
337 
338  // 現在位置と平行移動量をリセットする
339  instance->reset_translation();
340  break;
341 
342  case GLFW_KEY_UP:
343 
344  if (mods & GLFW_MOD_SHIFT)
345  current_if.arrow[1][1]++;
346  else if (mods & GLFW_MOD_CONTROL)
347  current_if.arrow[2][1]++;
348  else if (mods & GLFW_MOD_ALT)
349  current_if.arrow[3][1]++;
350  else
351  current_if.arrow[0][1]++;
352  break;
353 
354  case GLFW_KEY_DOWN:
355 
356  if (mods & GLFW_MOD_SHIFT)
357  current_if.arrow[1][1]--;
358  else if (mods & GLFW_MOD_CONTROL)
359  current_if.arrow[2][1]--;
360  else if (mods & GLFW_MOD_ALT)
361  current_if.arrow[3][1]--;
362  else
363  current_if.arrow[0][1]--;
364  break;
365 
366  case GLFW_KEY_RIGHT:
367 
368  if (mods & GLFW_MOD_SHIFT)
369  current_if.arrow[1][0]++;
370  else if (mods & GLFW_MOD_CONTROL)
371  current_if.arrow[2][0]++;
372  else if (mods & GLFW_MOD_ALT)
373  current_if.arrow[3][0]++;
374  else
375  current_if.arrow[0][0]++;
376  break;
377 
378  case GLFW_KEY_LEFT:
379 
380  if (mods & GLFW_MOD_SHIFT)
381  current_if.arrow[1][0]--;
382  else if (mods & GLFW_MOD_CONTROL)
383  current_if.arrow[2][0]--;
384  else if (mods & GLFW_MOD_ALT)
385  current_if.arrow[3][0]--;
386  else
387  current_if.arrow[0][0]--;
388  break;
389 
390  default:
391 
392  break;
393  }
394  }
395  }
396 
397  //
398  // マウスボタンを操作したときの処理
399  //
400  static void mouse(GLFWwindow *window, int button, int action, int mods)
401  {
402 #ifdef USE_IMGUI
403  // マウスカーソルが ImGui のウィンドウ上にあったら Window クラスのマウス位置を更新しない
404  if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) return;
405 #endif
406 
407  // このインスタンスの this ポインタを得る
408  Window* const instance(static_cast<Window*>(glfwGetWindowUserPointer(window)));
409 
410  // マウスボタンの状態を記録する
411  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
412  instance->status[button] = action != GLFW_RELEASE;
413 
414  if (instance)
415  {
416  // ユーザー定義のコールバック関数の呼び出し
417  if (instance->mouseFunc) (*instance->mouseFunc)(instance, button, action, mods);
418 
419  // 対象のユーザインタフェース
420  auto& current_if(instance->ui_data[instance->ui_no]);
421 
422  // マウスの現在位置を得る
423  const GLfloat x(current_if.mouse[0]);
424  const GLfloat y(current_if.mouse[1]);
425 
426  if (x < 0 || x >= instance->size[0] || y < 0 || y >= instance->size[1]) return;
427 
428  if (action)
429  {
430  // ドラッグ開始
431  current_if.trackball[button].begin(x, y);
432  }
433  else
434  {
435  // ドラッグ終了
436  current_if.location[button][0] = current_if.location[button][1];
437  current_if.translation[button][0] = current_if.translation[button][1];
438  current_if.trackball[button].end(x, y);
439  }
440  }
441  }
442 
443  //
444  // マウスホイールを操作した時の処理
445  //
446  static void wheel(GLFWwindow *window, double x, double y)
447  {
448 #ifdef USE_IMGUI
449  // マウスカーソルが ImGui のウィンドウ上にあったら Window クラスのマウスホイールの回転量を更新しない
450  if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) return;
451 #endif
452 
453  // このインスタンスの this ポインタを得る
454  Window *const instance(static_cast<Window *>(glfwGetWindowUserPointer(window)));
455 
456  if (instance)
457  {
458  // ユーザー定義のコールバック関数の呼び出し
459  if (instance->wheelFunc) (*instance->wheelFunc)(instance, x, y);
460 
461  // 対象のユーザインタフェース
462  auto& current_if(instance->ui_data[instance->ui_no]);
463 
464  // マウスホイールの回転量の保存
465  current_if.wheel[0] += static_cast<GLfloat>(x);
466  current_if.wheel[1] += static_cast<GLfloat>(y);
467 
468  // マウスによる平行移動量の z 値の更新
469  const GLfloat z(instance->getWheelY() * 0.5f);
470  for (auto& t : current_if.translation) t[1][2] = z;
471  }
472  }
473 
474  #ifdef USE_OCULUS_RIFT
475  //
476  // Oculus Rift の使用終了
477  //
478  void terminateLibOVR()
479  {
480  // ミラー表示用の FBO を削除する
481  if (mirrorFbo) glDeleteFramebuffers(1, &mirrorFbo);
482 
483  // ミラー表示に使ったテクスチャを開放する
484  if (mirrorTexture)
485  {
486 # if OVR_PRODUCT_VERSION > 0
487  ovr_DestroyMirrorTexture(session, mirrorTexture);
488 # else
489  glDeleteTextures(1, &mirrorTexture->OGL.TexId);
490  ovr_DestroyMirrorTexture(session, reinterpret_cast<ovrTexture *>(mirrorTexture));
491 # endif
492  }
493 
494  // Oculus Rift のレンダリング用の FBO を削除する
495  glDeleteFramebuffers(ovrEye_Count, oculusFbo);
496 
497  // Oculus Rift 表示用の FBO を削除する
498  for (int eye = 0; eye < ovrEye_Count; ++eye)
499  {
500 # if OVR_PRODUCT_VERSION > 0
501 
502  // レンダリングターゲットに使ったテクスチャを開放する
503  if (layerData.ColorTexture[eye])
504  {
505  ovr_DestroyTextureSwapChain(session, layerData.ColorTexture[eye]);
506  layerData.ColorTexture[eye] = nullptr;
507  }
508 
509  // デプスバッファとして使ったテクスチャを開放する
510  glDeleteTextures(1, &oculusDepth[eye]);
511  oculusDepth[eye] = 0;
512 
513 # else
514 
515  // レンダリングターゲットに使ったテクスチャを開放する
516  auto *const colorTexture(layerData.EyeFov.ColorTexture[eye]);
517  for (int i = 0; i < colorTexture->TextureCount; ++i)
518  {
519  const auto *const ctex(reinterpret_cast<ovrGLTexture *>(&colorTexture->Textures[i]));
520  glDeleteTextures(1, &ctex->OGL.TexId);
521  }
522  ovr_DestroySwapTextureSet(session, colorTexture);
523 
524  // デプスバッファとして使ったテクスチャを開放する
525  auto *const depthTexture(layerData.EyeFovDepth.DepthTexture[eye]);
526  for (int i = 0; i < depthTexture->TextureCount; ++i)
527  {
528  const auto *const dtex(reinterpret_cast<ovrGLTexture *>(&depthTexture->Textures[i]));
529  glDeleteTextures(1, &dtex->OGL.TexId);
530  }
531  ovr_DestroySwapTextureSet(session, depthTexture);
532 
533 # endif
534  }
535 
536  // Oculus Rift のセッションを破棄する
537  ovr_Destroy(session);
538  session = nullptr;
539 
540  // LibOVR を終了する
541  ovr_Shutdown();
542  }
543 #endif
544 
545  //
546  // GLFW のエラー表示
547  //
548  static void glfwErrorCallback(int error, const char *description)
549  {
550 #ifdef __aarch64__
551  if (error == 65544) return;
552 #endif
553  throw std::runtime_error(description);
554  }
555 
556 public:
557 
561  static void init(int major = 0, int minor = 1)
562  {
563  // 最初に実行するときだけ true
564  static bool firstTime(true);
565 
566  // 既に実行されていたら何もしない
567  if (!firstTime) return;
568 
569  // 初期化済みの印をつける
570  firstTime = false;
571 
572  // GLFW を初期化する
573  glfwSetErrorCallback(glfwErrorCallback);
574  if (glfwInit() == GL_FALSE) throw std::runtime_error("Can't initialize GLFW");
575 
576  // 後始末を登録する
577  atexit(glfwTerminate);
578 
579  // OpenGL の major 番号が指定されていれば
580  if (major > 0)
581  {
582  // OpenGL のバージョンを指定する
583  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major);
584  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor);
585 
586  if (major * 10 + minor >= 32)
587  {
588  // Core Profile を選択する
589  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
590  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
591  }
592  }
593 
594 #ifdef USE_OCULUS_RIFT
595  // Oculus Rift (LibOVR) を初期化する
596  ovrInitParams initParams = { ovrInit_RequestVersion, OVR_MINOR_VERSION, NULL, 0, 0 };
597  if (OVR_FAILURE(ovr_Initialize(&initParams))) throw std::runtime_error("Can't initialize LibOVR");
598 
599  // プログラム終了時に LibOVR を終了する
600  atexit(terminateLibOVR);
601 
602  // Oculus Rift のセッションを作成する
603  ovrGraphicsLuid luid;
604  session = nullptr;
605  if (OVR_FAILURE(ovr_Create(&session, &luid))) throw std::runtime_error("Can't create Oculus Rift session");
606 
607  // Oculus Rift へのレンダリングに使う FBO の初期値を設定する
608  for (int eye = 0; eye < ovrEye_Count; ++eye) oculusFbo[eye] = 0;
609 
610  // ミラー表示に使う FBO の初期値を設定する
611  mirrorFbo = 0;
612  mirrorTexture = nullptr;
613 
614 # if OVR_PRODUCT_VERSION > 0
615  // デフォルトのグラフィックスアダプタが使われているか確かめる
616  if (Compare(luid, GetDefaultAdapterLuid())) throw std::runtime_error("Graphics adapter is not default");
617 
618  // Asynchronous TimeWarp 処理に使うフレーム番号の初期値を設定する
619  frameIndex = 0LL;
620 
621  // Oculus Rift へのレンダリングに使う FBO のデプステクスチャの初期値を設定する
622  for (int eye = 0; eye < ovrEye_Count; ++eye) oculusDepth[eye] = 0;
623 # endif
624 
625  // Oculus Rift ではダブルバッファリングしない
626  glfwWindowHint(GLFW_DOUBLEBUFFER, GL_FALSE);
627 
628  // Oculus Rift では SRGB でレンダリングする
629  glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
630 #endif
631 
632 #ifdef USE_IMGUI
633  // ImGui のバージョンをチェックする
634  IMGUI_CHECKVERSION();
635 
636  // ImGui のコンテキストを作成する
637  ImGui::CreateContext();
638 
639  // プログラム終了時には ImGui のコンテキストを破棄する
640  atexit([] { ImGui::DestroyContext(); });
641 #endif
642  }
643 
650  Window(const char *title = "GLFW Window", int width = 640, int height = 480,
651  int fullscreen = 0, GLFWwindow *share = nullptr)
652  : window(nullptr), size{ width, height }, aspect(1.0f)
653  , velocity{ 1.0f, 1.0f }
654  , status{ false }
655  , ui_no(0)
656  , userPointer(nullptr)
657  , resizeFunc(nullptr)
658  , keyboardFunc(nullptr)
659  , mouseFunc(nullptr)
660  , wheelFunc(nullptr)
661  {
662  // ディスプレイの情報
663  GLFWmonitor *monitor(nullptr);
664 
665  // フルスクリーン表示
666  if (fullscreen > 0)
667  {
668  // 接続されているモニタの数を数える
669  int mcount;
670  GLFWmonitor **const monitors(glfwGetMonitors(&mcount));
671 
672  // セカンダリモニタがあればそれを使う
673  if (fullscreen > mcount) fullscreen = mcount;
674  monitor = monitors[fullscreen - 1];
675 
676  // モニタのモードを調べる
677  const GLFWvidmode *mode(glfwGetVideoMode(monitor));
678 
679  // ウィンドウのサイズをディスプレイのサイズにする
680  width = mode->width;
681  height = mode->height;
682  }
683 
684  // GLFW のウィンドウを作成する
685  window = glfwCreateWindow(width, height, title, monitor, share);
686 
687  // ウィンドウが作成できなければエラー
688  if (!window) throw std::runtime_error("Unable to open the GLFW window.");
689 
690  // 現在のウィンドウを処理対象にする
691  glfwMakeContextCurrent(window);
692 
693  // ゲームグラフィックス特論の都合による初期化を行う
694  ggInit();
695 
696  // このインスタンスの this ポインタを記録しておく
697  glfwSetWindowUserPointer(window, this);
698 
699  // キーボードを操作した時の処理を登録する
700  glfwSetKeyCallback(window, keyboard);
701 
702  // マウスボタンを操作したときの処理を登録する
703  glfwSetMouseButtonCallback(window, mouse);
704 
705  // マウスホイール操作時に呼び出す処理を登録する
706  glfwSetScrollCallback(window, wheel);
707 
708  // ウィンドウのサイズ変更時に呼び出す処理を登録する
709  glfwSetFramebufferSizeCallback(window, resize);
710 
711 #ifdef USE_OCULUS_RIFT
712  // Oculus Rift の情報を取り出す
713  hmdDesc = ovr_GetHmdDesc(session);
714 
715 # ifdef _DEBUG
716  // Oculus Rift の情報を表示する
717  std::cerr
718  << "\nProduct name: " << hmdDesc.ProductName
719  << "\nResolution: " << hmdDesc.Resolution.w << " x " << hmdDesc.Resolution.h
720  << "\nDefault Fov: (" << hmdDesc.DefaultEyeFov[ovrEye_Left].LeftTan
721  << "," << hmdDesc.DefaultEyeFov[ovrEye_Left].DownTan
722  << ") - (" << hmdDesc.DefaultEyeFov[ovrEye_Left].RightTan
723  << "," << hmdDesc.DefaultEyeFov[ovrEye_Left].UpTan
724  << ")\n (" << hmdDesc.DefaultEyeFov[ovrEye_Right].LeftTan
725  << "," << hmdDesc.DefaultEyeFov[ovrEye_Right].DownTan
726  << ") - (" << hmdDesc.DefaultEyeFov[ovrEye_Right].RightTan
727  << "," << hmdDesc.DefaultEyeFov[ovrEye_Right].UpTan
728  << ")\nMaximum Fov: (" << hmdDesc.MaxEyeFov[ovrEye_Left].LeftTan
729  << "," << hmdDesc.MaxEyeFov[ovrEye_Left].DownTan
730  << ") - (" << hmdDesc.MaxEyeFov[ovrEye_Left].RightTan
731  << "," << hmdDesc.MaxEyeFov[ovrEye_Left].UpTan
732  << ")\n (" << hmdDesc.MaxEyeFov[ovrEye_Right].LeftTan
733  << "," << hmdDesc.MaxEyeFov[ovrEye_Right].DownTan
734  << ") - (" << hmdDesc.MaxEyeFov[ovrEye_Right].RightTan
735  << "," << hmdDesc.MaxEyeFov[ovrEye_Right].UpTan
736  << ")\n" << std::endl;
737 # endif
738 
739  // Oculus Rift に転送する描画データを作成する
740 # if OVR_PRODUCT_VERSION > 0
741  layerData.Header.Type = ovrLayerType_EyeFov;
742 # else
743  layerData.Header.Type = ovrLayerType_EyeFovDepth;
744 # endif
745  layerData.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; // OpenGL なので左下が原点
746 
747  // Oculus Rift 表示用の FBO を作成する
748  for (int eye = 0; eye < ovrEye_Count; ++eye)
749  {
750  // Oculus Rift の視野を取得する
751  const auto &fov(hmdDesc.DefaultEyeFov[ovrEyeType(eye)]);
752 
753  // Oculus Rift 表示用の FBO のサイズを求める
754  const auto textureSize(ovr_GetFovTextureSize(session, ovrEyeType(eye), fov, 1.0f));
755 
756  // Oculus Rift 表示用の FBO のアスペクト比を求める
757  aspect = static_cast<GLfloat>(textureSize.w) / static_cast<GLfloat>(textureSize.h);
758 
759  // Oculus Rift のスクリーンのサイズを保存する
760  screen[eye][0] = -fov.LeftTan;
761  screen[eye][1] = fov.RightTan;
762  screen[eye][2] = -fov.DownTan;
763  screen[eye][3] = fov.UpTan;
764 
765 # if OVR_PRODUCT_VERSION > 0
766 
767  // 描画データに視野を設定する
768  layerData.Fov[eye] = fov;
769 
770  // 描画データにビューポートを設定する
771  layerData.Viewport[eye].Pos = OVR::Vector2i(0, 0);
772  layerData.Viewport[eye].Size = textureSize;
773 
774  // Oculus Rift 表示用の FBO のカラーバッファとして使うテクスチャセットの特性
775  const ovrTextureSwapChainDesc colorDesc =
776  {
777  ovrTexture_2D, // Type
778  OVR_FORMAT_R8G8B8A8_UNORM_SRGB, // Format
779  1, // ArraySize
780  textureSize.w, // Width
781  textureSize.h, // Height
782  1, // MipLevels
783  1, // SampleCount
784  ovrFalse, // StaticImage
785  0, 0
786  };
787 
788  // Oculus Rift 表示用の FBO のレンダーターゲットとして使うテクスチャチェインを作成する
789  layerData.ColorTexture[eye] = nullptr;
790  if (OVR_SUCCESS(ovr_CreateTextureSwapChainGL(session, &colorDesc, &layerData.ColorTexture[eye])))
791  {
792  // 作成したテクスチャチェインの長さを取得する
793  int length(0);
794  if (OVR_SUCCESS(ovr_GetTextureSwapChainLength(session, layerData.ColorTexture[eye], &length)))
795  {
796  // テクスチャチェインの個々の要素について
797  for (int i = 0; i < length; ++i)
798  {
799  // テクスチャのパラメータを設定する
800  GLuint texId;
801  ovr_GetTextureSwapChainBufferGL(session, layerData.ColorTexture[eye], i, &texId);
802  glBindTexture(GL_TEXTURE_2D, texId);
803  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
804  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
805  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
806  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
807  }
808  }
809 
810  // Oculus Rift 表示用の FBO のデプスバッファとして使うテクスチャを作成する
811  glGenTextures(1, &oculusDepth[eye]);
812  glBindTexture(GL_TEXTURE_2D, oculusDepth[eye]);
813  glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, textureSize.w, textureSize.h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
814  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
815  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
816  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
817  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
818  }
819 
820 # else
821 
822  // 描画データに視野を設定する
823  layerData.EyeFov.Fov[eye] = fov;
824 
825  // 描画データにビューポートを設定する
826  layerData.EyeFov.Viewport[eye].Pos = OVR::Vector2i(0, 0);
827  layerData.EyeFov.Viewport[eye].Size = textureSize;
828 
829  // Oculus Rift 表示用の FBO のカラーバッファとして使うテクスチャセットを作成する
830  ovrSwapTextureSet *colorTexture;
831  ovr_CreateSwapTextureSetGL(session, GL_SRGB8_ALPHA8, textureSize.w, textureSize.h, &colorTexture);
832  layerData.EyeFov.ColorTexture[eye] = colorTexture;
833 
834  // Oculus Rift 表示用の FBO のデプスバッファとして使うテクスチャセットを作成する
835  ovrSwapTextureSet *depthTexture;
836  ovr_CreateSwapTextureSetGL(session, GL_DEPTH_COMPONENT32F, textureSize.w, textureSize.h, &depthTexture);
837  layerData.EyeFovDepth.DepthTexture[eye] = depthTexture;
838 
839  // Oculus Rift のレンズ補正等の設定値を取得する
840  eyeRenderDesc[eye] = ovr_GetRenderDesc(session, ovrEyeType(eye), fov);
841 
842 # endif
843  }
844 
845 # if OVR_PRODUCT_VERSION > 0
846 
847  // 姿勢のトラッキングにおける床の高さを 0 に設定する
848  ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
849 
850  // ミラー表示用の FBO を作成する
851  const ovrMirrorTextureDesc mirrorDesc =
852  {
853  OVR_FORMAT_R8G8B8A8_UNORM_SRGB, // Format
854  mirrorWidth = width, // Width
855  mirrorHeight = height, // Height
856  0 // Flags
857  };
858 
859  // ミラー表示用の FBO のカラーバッファとして使うテクスチャを作成する
860  if (OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirrorTexture)))
861  {
862  // 作成したテクスチャのテクスチャ名を得る
863  GLuint texId;
864  if (OVR_SUCCESS(ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &texId)))
865  {
866  // 作成したテクスチャをミラー表示用の FBO にカラーバッファとして組み込む
867  glGenFramebuffers(1, &mirrorFbo);
868  glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFbo);
869  glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
870  glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
871  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
872  }
873  }
874 
875 # else
876 
877  // ミラー表示用の FBO を作成する
878  if (OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, GL_SRGB8_ALPHA8, width, height, reinterpret_cast<ovrTexture **>(&mirrorTexture))))
879  {
880  glGenFramebuffers(1, &mirrorFbo);
881  glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFbo);
882  glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTexture->OGL.TexId, 0);
883  glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
884  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
885  }
886 
887 # endif
888 
889  // Oculus Rift のレンダリング用の FBO を作成する
890  glGenFramebuffers(ovrEye_Count, oculusFbo);
891 
892  // Oculus Rift にレンダリングするときは sRGB カラースペースを使う
893  glEnable(GL_FRAMEBUFFER_SRGB);
894 
895  // Oculus Rift への表示では垂直同期タイミングに合わせない
896  glfwSwapInterval(0);
897 
898 #else
899 
900  // 垂直同期タイミングに合わせる
901  glfwSwapInterval(1);
902 
903 #endif
904 
905  // ビューポートと投影変換行列を初期化する
906  resize(window, width, height);
907 
908 #ifdef USE_IMGUI
909  // Setup Platform/Renderer bindings
910  ImGui_ImplGlfw_InitForOpenGL(window, true);
911  ImGui_ImplOpenGL3_Init(nullptr);
912 #endif
913  }
914 
916  Window(const Window &w) = delete;
917 
919  Window &operator=(const Window &w) = delete;
920 
922  virtual ~Window()
923  {
924  // ウィンドウが作成されていなければ戻る
925  if (!window) return;
926 
927 #ifdef USE_IMGUI
928  // Shutdown Platform/Renderer bindings
929  ImGui_ImplOpenGL3_Shutdown();
930  ImGui_ImplGlfw_Shutdown();
931 #endif
932 
933  // ウィンドウを破棄する
934  glfwDestroyWindow(window);
935  }
936 
937 #ifdef USE_OCULUS_RIFT
938 
941  bool begin()
942  {
943 # if OVR_PRODUCT_VERSION > 0
944 
945  // セッションの状態を取得する
946  ovrSessionStatus sessionStatus;
947  ovr_GetSessionStatus(session, &sessionStatus);
948 
949  // アプリケーションが終了を要求しているときはウィンドウのクローズフラグを立てる
950  if (sessionStatus.ShouldQuit) setClose(GLFW_TRUE);
951 
952  // Oculus Rift に表示されていないときは戻る
953  if (!sessionStatus.IsVisible) return false;
954 
955  // 現在の状態をトラッキングの原点にする
956  if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session);
957 
958  // HmdToEyeOffset などは実行時に変化するので毎フレーム ovr_GetRenderDesc() で ovrEyeRenderDesc を取得する
959  const ovrEyeRenderDesc eyeRenderDesc[] =
960  {
961  ovr_GetRenderDesc(session, ovrEyeType(0), hmdDesc.DefaultEyeFov[0]),
962  ovr_GetRenderDesc(session, ovrEyeType(1), hmdDesc.DefaultEyeFov[1])
963  };
964 
965  // Oculus Rift のスクリーンのヘッドトラッキング位置からの変位を取得する
966  const ovrPosef hmdToEyePose[] =
967  {
968  eyeRenderDesc[0].HmdToEyePose,
969  eyeRenderDesc[1].HmdToEyePose
970  };
971 
972  // 視点の姿勢情報を取得する
973  ovr_GetEyePoses(session, frameIndex, ovrTrue, hmdToEyePose, layerData.RenderPose, &layerData.SensorSampleTime);
974 
975 # else
976 
977  // フレームのタイミング計測開始
978  const auto ftiming(ovr_GetPredictedDisplayTime(session, 0));
979 
980  // sensorSampleTime の取得は可能な限り ovr_GetTrackingState() の近くで行う
981  layerData.EyeFov.SensorSampleTime = ovr_GetTimeInSeconds();
982 
983  // ヘッドトラッキングの状態を取得する
984  const auto hmdState(ovr_GetTrackingState(session, ftiming, ovrTrue));
985 
986  // Oculus Rift のスクリーンのヘッドトラッキング位置からの変位を取得する
987  const ovrVector3f hmdToEyeViewOffset[] =
988  {
989  eyeRenderDesc[0].HmdToEyeViewOffset,
990  eyeRenderDesc[1].HmdToEyeViewOffset
991  };
992 
993  // 視点の姿勢情報を求める
994  ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewOffset, eyePose);
995 
996 # endif
997 
998  return true;
999  }
1000 
1006  void select(int eye, GLfloat *screen, GLfloat *position, GLfloat *orientation)
1007  {
1008 # if OVR_PRODUCT_VERSION > 0
1009 
1010  // Oculus Rift にレンダリングする FBO に切り替える
1011  if (layerData.ColorTexture[eye])
1012  {
1013  // FBO のカラーバッファに使う現在のテクスチャのインデックスを取得する
1014  int curIndex;
1015  ovr_GetTextureSwapChainCurrentIndex(session, layerData.ColorTexture[eye], &curIndex);
1016 
1017  // FBO のカラーバッファに使うテクスチャを取得する
1018  GLuint curTexId;
1019  ovr_GetTextureSwapChainBufferGL(session, layerData.ColorTexture[eye], curIndex, &curTexId);
1020 
1021  // FBO を設定する
1022  glBindFramebuffer(GL_FRAMEBUFFER, oculusFbo[eye]);
1023  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, curTexId, 0);
1024  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, oculusDepth[eye], 0);
1025 
1026  // ビューポートを設定する
1027  const auto &vp(layerData.Viewport[eye]);
1028  glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
1029  }
1030 
1031  // Oculus Rift の片目の位置と回転を取得する
1032  const auto &p(layerData.RenderPose[eye].Position);
1033  const auto &o(layerData.RenderPose[eye].Orientation);
1034 
1035 # else
1036 
1037  // レンダーターゲットに描画する前にレンダーターゲットのインデックスをインクリメントする
1038  auto *const colorTexture(layerData.EyeFov.ColorTexture[eye]);
1039  colorTexture->CurrentIndex = (colorTexture->CurrentIndex + 1) % colorTexture->TextureCount;
1040  auto *const depthTexture(layerData.EyeFovDepth.DepthTexture[eye]);
1041  depthTexture->CurrentIndex = (depthTexture->CurrentIndex + 1) % depthTexture->TextureCount;
1042 
1043  // レンダーターゲットを切り替える
1044  glBindFramebuffer(GL_FRAMEBUFFER, oculusFbo[eye]);
1045  const auto &ctex(reinterpret_cast<ovrGLTexture *>(&colorTexture->Textures[colorTexture->CurrentIndex]));
1046  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ctex->OGL.TexId, 0);
1047  const auto &dtex(reinterpret_cast<ovrGLTexture *>(&depthTexture->Textures[depthTexture->CurrentIndex]));
1048  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dtex->OGL.TexId, 0);
1049 
1050  // ビューポートを設定する
1051  const auto &vp(layerData.EyeFov.Viewport[eye]);
1052  glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
1053 
1054  // Oculus Rift の片目の位置と回転を取得する
1055  const auto &p(eyePose[eye].Position);
1056  const auto &o(eyePose[eye].Orientation);
1057 
1058 # endif
1059 
1060  // Oculus Rift のスクリーンの大きさを返す
1061  screen[0] = this->screen[eye][0];
1062  screen[1] = this->screen[eye][1];
1063  screen[2] = this->screen[eye][2];
1064  screen[3] = this->screen[eye][3];
1065 
1066  // Oculus Rift の位置を返す
1067  position[0] = p.x;
1068  position[1] = p.y;
1069  position[2] = p.z;
1070 
1071  // Oculus Rift の方向を返す
1072  orientation[0] = o.x;
1073  orientation[1] = o.y;
1074  orientation[2] = o.z;
1075  orientation[3] = o.w;
1076  }
1077 
1080  void timewarp(const GgMatrix &projection)
1081  {
1082 # if OVR_PRODUCT_VERSION < 1
1083  // TimeWarp に使う変換行列の成分を設定する
1084  auto &posTimewarpProjectionDesc(layerData.EyeFovDepth.ProjectionDesc);
1085  posTimewarpProjectionDesc.Projection22 = (projection.get()[4 * 2 + 2] + projection.get()[4 * 3 + 2]) * 0.5f;
1086  posTimewarpProjectionDesc.Projection23 = projection.get()[4 * 2 + 3] * 0.5f;
1087  posTimewarpProjectionDesc.Projection32 = projection.get()[4 * 3 + 2];
1088 # endif
1089  }
1090 
1093  void commit(int eye)
1094  {
1095 # if OVR_PRODUCT_VERSION > 0
1096  // GL_COLOR_ATTACHMENT0 に割り当てられたテクスチャが wglDXUnlockObjectsNV() によって
1097  // アンロックされるために次のフレームの処理において無効な GL_COLOR_ATTACHMENT0 が
1098  // FBO に結合されるのを避ける
1099  glBindFramebuffer(GL_FRAMEBUFFER, oculusFbo[eye]);
1100  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
1101  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
1102 
1103  // 保留中の変更を layerData.ColorTexture[eye] に反映しインデックスを更新する
1104  ovr_CommitTextureSwapChain(session, layerData.ColorTexture[eye]);
1105 # endif
1106  }
1107 
1110  void submit(bool mirror = true)
1111  {
1112  // エラーチェック
1113  ggError();
1114 
1115 # if OVR_PRODUCT_VERSION > 0
1116  // 描画データを Oculus Rift に転送する
1117  const auto *const layers(&layerData.Header);
1118  if (OVR_FAILURE(ovr_SubmitFrame(session, frameIndex++, nullptr, &layers, 1)))
1119 # else
1120  // Oculus Rift 上の描画位置と拡大率を求める
1121  ovrViewScaleDesc viewScaleDesc;
1122  viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
1123  viewScaleDesc.HmdToEyeViewOffset[0] = eyeRenderDesc[0].HmdToEyeViewOffset;
1124  viewScaleDesc.HmdToEyeViewOffset[1] = eyeRenderDesc[1].HmdToEyeViewOffset;
1125 
1126  // 描画データを更新する
1127  layerData.EyeFov.RenderPose[0] = eyePose[0];
1128  layerData.EyeFov.RenderPose[1] = eyePose[1];
1129 
1130  // 描画データを Oculus Rift に転送する
1131  const auto *const layers(&layerData.Header);
1132  if (OVR_FAILURE(ovr_SubmitFrame(session, 0, &viewScaleDesc, &layers, 1)))
1133 # endif
1134  {
1135  // 転送に失敗したら Oculus Rift の設定を最初からやり直す必要があるらしい
1136  // けどめんどくさいのでウィンドウを閉じてしまう
1137  setClose(GLFW_TRUE);
1138  }
1139 
1140  // ミラー表示
1141  if (mirror)
1142  {
1143  // レンダリング結果をミラー表示用のフレームバッファにも転送する
1144  glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFbo);
1145  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1146 # if OVR_PRODUCT_VERSION > 0
1147  glBlitFramebuffer(0, size[1], size[0], 0, 0, 0, size[0], size[1], GL_COLOR_BUFFER_BIT, GL_NEAREST);
1148 # else
1149  const auto w(mirrorTexture->OGL.Header.TextureSize.w);
1150  const auto h(mirrorTexture->OGL.Header.TextureSize.h);
1151  glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1152 # endif
1153  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
1154 
1155 #ifdef USE_IMGUI
1156  // ImGui のフレームをレンダリングする
1157  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1158 #endif
1159 
1160  // 残っている OpenGL コマンドを実行する
1161  glFlush();
1162  }
1163  }
1164 
1166  const int eyeCount = ovrEye_Count;
1167 
1168 #else
1169 
1171  const int eyeCount = 1;
1172 
1173 #endif
1174 
1177  GLFWwindow *get() const
1178  {
1179  return window;
1180  }
1181 
1184  void setClose(int flag = GLFW_TRUE) const
1185  {
1186  glfwSetWindowShouldClose(window, flag);
1187  }
1188 
1191  bool shouldClose() const
1192  {
1193  // ウィンドウを閉じるべきなら true を返す
1194  return glfwWindowShouldClose(window) != GLFW_FALSE;
1195  }
1196 
1199  operator bool()
1200  {
1201  // イベントを取り出す
1202  glfwPollEvents();
1203 
1204  // ウィンドウを閉じるべきなら false を返す
1205  if (shouldClose()) return false;
1206 
1207  // 対象のユーザインタフェース
1208  auto& current_if(ui_data[ui_no]);
1209 
1210 #ifdef USE_IMGUI
1211 
1212  // ImGui の新規フレームを作成する
1213  ImGui_ImplOpenGL3_NewFrame();
1214  ImGui_ImplGlfw_NewFrame();
1215 
1216  // マウスカーソルが ImGui のウィンドウ上にあったら Window クラスのマウス位置を更新しない
1217  if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) return true;
1218 
1219  // マウスの現在位置を調べる
1220  const ImGuiIO &io(ImGui::GetIO());
1221 
1222  // マウスの位置を更新する
1223  current_if.mouse = std::array<GLfloat, 2>{ io.MousePos.x, io.MousePos.y };
1224 
1225 #else
1226 
1227  // マウスの現在位置を調べる
1228  double x, y;
1229  glfwGetCursorPos(window, &x, &y);
1230 
1231  // マウスの位置を更新する
1232  current_if.mouse = std::array<GLfloat, 2>{ static_cast<GLfloat>(x), static_cast<GLfloat>(y) };
1233 
1234 #endif
1235 
1236  // マウスドラッグ
1237  for (int button = GLFW_MOUSE_BUTTON_1; button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT; ++button)
1238  {
1239  // マウスボタンを押していたら
1240  if (status[button])
1241  {
1242  // 現在位置と平行移動量を更新する
1243  current_if.calcTranslation(button, velocity);
1244  }
1245  }
1246 
1247  return true;
1248  }
1249 
1252  {
1253 #ifdef USE_IMGUI
1254  // ImGui のフレームをレンダリングする
1255  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1256 #endif
1257 
1258  // エラーチェック
1259  ggError();
1260 
1261  // カラーバッファを入れ替える
1262  glfwSwapBuffers(window);
1263  }
1264 
1267  GLsizei getWidth() const
1268  {
1269  return size[0];
1270  }
1271 
1274  GLsizei getHeight() const
1275  {
1276  return size[1];
1277  }
1278 
1281  const GLsizei *getSize() const
1282  {
1283  return size.data();
1284  }
1285 
1288  void getSize(GLsizei *size) const
1289  {
1290  size[0] = getWidth();
1291  size[1] = getHeight();
1292  }
1293 
1296  GLfloat getAspect() const
1297  {
1298  return aspect;
1299  }
1300 
1303  {
1304 #ifndef USE_OCULUS_RIFT
1305  // ウィンドウ全体に描画する
1306  glViewport(0, 0, fboSize[0], fboSize[1]);
1307 #endif
1308  }
1309 
1312  bool getKey(int key)
1313  {
1314 #ifdef USE_IMGUI
1315  // ImGui のウィンドウが選択されていたらキーボードの処理を行わない
1316  if (ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)) return false;
1317 #endif
1318 
1319  return glfwGetKey(window, key) != GLFW_RELEASE;
1320  }
1321 
1324  void selectInterface(int no)
1325  {
1326  assert(static_cast<size_t>(no) < ui_data.size());
1327  ui_no = no;
1328  }
1329 
1333  void setVelocity(GLfloat vx, GLfloat vy)
1334  {
1335  velocity = std::array<GLfloat, 2>{ vx, vy };
1336  }
1337 
1342  GLfloat getArrow(int direction = 0, int mods = 0) const
1343  {
1344  const auto& current_if(ui_data[ui_no]);
1345  return static_cast<GLfloat>(current_if.arrow[mods & 3][direction & 1]);
1346  }
1347 
1351  GLfloat getArrowX(int mods = 0) const
1352  {
1353  return getArrow(0, mods);
1354  }
1355 
1359  GLfloat getArrowY(int mods = 0) const
1360  {
1361  return getArrow(1, mods);
1362  }
1363 
1367  void getArrow(GLfloat *arrow, int mods = 0) const
1368  {
1369  arrow[0] = getArrowX(mods);
1370  arrow[1] = getArrowY(mods);
1371  }
1372 
1375  GLfloat getShiftArrowX() const
1376  {
1377  return getArrow(0, 1);
1378  }
1379 
1382  GLfloat getShiftArrowY() const
1383  {
1384  return getArrow(1, 1);
1385  }
1386 
1389  void getShiftArrow(GLfloat *shift_arrow) const
1390  {
1391  shift_arrow[0] = getShiftArrowX();
1392  shift_arrow[1] = getShiftArrowY();
1393  }
1394 
1397  GLfloat getControlArrowX() const
1398  {
1399  return getArrow(0, 2);
1400  }
1401 
1404  GLfloat getControlArrowY() const
1405  {
1406  return getArrow(1, 2);
1407  }
1408 
1411  void getControlArrow(GLfloat *control_arrow) const
1412  {
1413  control_arrow[0] = getControlArrowX();
1414  control_arrow[1] = getControlArrowY();
1415  }
1416 
1419  GLfloat getAltArrowX() const
1420  {
1421  return getArrow(0, 3);
1422  }
1423 
1426  GLfloat getAltArrowY() const
1427  {
1428  return getArrow(1, 3);
1429  }
1430 
1433  void getAltlArrow(GLfloat *alt_arrow) const
1434  {
1435  alt_arrow[0] = getAltArrowX();
1436  alt_arrow[1] = getAltArrowY();
1437  }
1438 
1441  const GLfloat *getMouse() const
1442  {
1443  const auto& current_if(ui_data[ui_no]);
1444  return current_if.mouse.data();
1445  }
1446 
1449  void getMouse(GLfloat *position) const
1450  {
1451  const auto& current_if(ui_data[ui_no]);
1452  position[0] = current_if.mouse[0];
1453  position[1] = current_if.mouse[1];
1454  }
1455 
1459  const GLfloat getMouse(int direction) const
1460  {
1461  const auto& current_if(ui_data[ui_no]);
1462  return current_if.mouse[direction & 1];
1463  }
1464 
1467  GLfloat getMouseX() const
1468  {
1469  const auto& current_if(ui_data[ui_no]);
1470  return current_if.mouse[0];
1471  }
1472 
1475  GLfloat getMouseY() const
1476  {
1477  const auto& current_if(ui_data[ui_no]);
1478  return current_if.mouse[1];
1479  }
1480 
1483  const GLfloat *getWheel() const
1484  {
1485  const auto& current_if(ui_data[ui_no]);
1486  return current_if.wheel.data();
1487  }
1488 
1491  void getWheel(GLfloat *rotation) const
1492  {
1493  const auto& current_if(ui_data[ui_no]);
1494  rotation[0] = current_if.wheel[0];
1495  rotation[1] = current_if.wheel[1];
1496  }
1497 
1501  GLfloat getWheel(int direction) const
1502  {
1503  const auto& current_if(ui_data[ui_no]);
1504  return current_if.wheel[direction & 1];
1505  }
1506 
1508  const GLfloat getWheelX() const
1509  {
1510  const auto& current_if(ui_data[ui_no]);
1511  return current_if.wheel[0];
1512  }
1513 
1515  const GLfloat getWheelY() const
1516  {
1517  const auto& current_if(ui_data[ui_no]);
1518  return current_if.wheel[1];
1519  }
1520 
1524  const GLfloat* getLocation(int button = GLFW_MOUSE_BUTTON_1) const
1525  {
1526  const auto& current_if(ui_data[ui_no]);
1527  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
1528  return current_if.location[button][1].data();
1529  }
1530 
1534  GgMatrix getTranslation(int button = GLFW_MOUSE_BUTTON_1) const
1535  {
1536  const auto& current_if(ui_data[ui_no]);
1537  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
1538  return ggTranslate(current_if.translation[button][1].data());
1539  }
1540 
1544  GgQuaternion getTrackballData(int button = GLFW_MOUSE_BUTTON_1) const
1545  {
1546  const auto& current_if(ui_data[ui_no]);
1547  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
1548  return current_if.trackball[button].getQuaternion();
1549  }
1550 
1554  GgMatrix getTrackball(int button = GLFW_MOUSE_BUTTON_1) const
1555  {
1556  const auto& current_if(ui_data[ui_no]);
1557  assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 + BUTTON_COUNT);
1558  return current_if.trackball[button].getMatrix();
1559  }
1560 
1563  {
1564  // トラックボールをリセットする
1565  for (auto& tb : ui_data[ui_no].trackball)
1566  {
1567  tb.reset();
1568  }
1569  }
1570 
1573  {
1574  // 現在のインターフェース
1575  auto& current_if(ui_data[ui_no]);
1576 
1577  // 現在位置をリセットする
1578  for (auto& l : current_if.location)
1579  {
1580  std::fill(l.begin(), l.end(), std::array<GLfloat, 2>{ 0.0f, 0.0f });
1581  }
1582 
1583  // 平行移動量をリセットする
1584  for (auto& t : current_if.translation)
1585  {
1586  std::fill(t.begin(), t.end(), std::array<GLfloat, 3>{ 0.0f, 0.0f, 0.0f });
1587  }
1588 
1589  // 矢印キーの設定値をリセットする
1590  std::fill(current_if.arrow.begin(), current_if.arrow.end(), std::array<int, 2>{ 0, 0 });
1591 
1592  // マウスホイールの回転量をリセットする
1593  std::fill(current_if.wheel.begin(), current_if.wheel.end(), 0.0f);
1594  }
1595 
1597  void reset()
1598  {
1599  // トラックボール処理をリセットする
1600  reset_trackball();
1601 
1602  // 平行移動量をリセットする
1603  reset_translation();
1604  }
1605 
1608  void *getUserPointer() const
1609  {
1610  return userPointer;
1611  }
1612 
1615  void setUserPointer(void *pointer)
1616  {
1617  userPointer = pointer;
1618  }
1619 
1622  void setResizeFunc(void (*func)(const Window *window, int width, int height))
1623  {
1624  resizeFunc = func;
1625  }
1626 
1629  void setKeyboardFunc(void (*func)(const Window *window, int key, int scancode, int action, int mods))
1630  {
1631  keyboardFunc = func;
1632  }
1633 
1636  void setMouseFunc(void (*func)(const Window *window, int button, int action, int mods))
1637  {
1638  mouseFunc = func;
1639  }
1640 
1643  void setResizeFunc(void (*func)(const Window *window, double x, double y))
1644  {
1645  wheelFunc = func;
1646  }
1647 };
constexpr int BUTTON_COUNT(3)
constexpr int INTERFACE_COUNT(3)
ウィンドウ関連の処理.
Definition: Window.h:89
void reset()
トラックボール・マウスホイール・矢印キーの値を初期化する
Definition: Window.h:1597
void * getUserPointer() const
ユーザーポインタを取り出す.
Definition: Window.h:1608
GLfloat getShiftArrowX() const
SHIFT キーを押しながら矢印キーを押したときの現在の X 値を得る.
Definition: Window.h:1375
void getSize(GLsizei *size) const
ウィンドウのサイズを得る.
Definition: Window.h:1288
Window(const Window &w)=delete
コピーコンストラクタは使用禁止.
void reset_translation()
現在位置と平行移動量をリセットする
Definition: Window.h:1572
void setVelocity(GLfloat vx, GLfloat vy)
マウスの移動速度を設定する
Definition: Window.h:1333
void getMouse(GLfloat *position) const
マウスカーソルの現在位置を得る.
Definition: Window.h:1449
bool shouldClose() const
ウィンドウを閉じるべきかどうか調べる.
Definition: Window.h:1191
GLfloat getAspect() const
ウィンドウのアスペクト比を得る.
Definition: Window.h:1296
void setResizeFunc(void(*func)(const Window *window, double x, double y))
ユーザ定義の wheel 関数を設定する.
Definition: Window.h:1643
void getShiftArrow(GLfloat *shift_arrow) const
SHIFT キーを押しながら矢印キーを押したときの現在の値を得る.
Definition: Window.h:1389
GLFWwindow * get() const
ウィンドウの識別子のポインタを取得する.
Definition: Window.h:1177
void reset_trackball()
トラックボール処理をリセットする
Definition: Window.h:1562
const GLsizei * getSize() const
ウィンドウのサイズを得る.
Definition: Window.h:1281
const GLfloat getMouse(int direction) const
マウスカーソルの現在位置を得る.
Definition: Window.h:1459
const GLfloat * getWheel() const
マウスホイールの回転量を得る.
Definition: Window.h:1483
void getWheel(GLfloat *rotation) const
マウスホイールの回転量を得る.
Definition: Window.h:1491
void setResizeFunc(void(*func)(const Window *window, int width, int height))
ユーザ定義の resize 関数を設定する.
Definition: Window.h:1622
void getArrow(GLfloat *arrow, int mods=0) const
矢印キーの現在の値を得る.
Definition: Window.h:1367
const GLfloat * getMouse() const
マウスカーソルの現在位置を得る.
Definition: Window.h:1441
Window & operator=(const Window &w)=delete
代入演算子は使用禁止.
static void init(int major=0, int minor=1)
初期化, 最初に一度だけ実行する.
Definition: Window.h:561
GLfloat getShiftArrowY() const
SHIFT キーを押しながら矢印キーを押したときの現在の Y 値を得る.
Definition: Window.h:1382
GLsizei getWidth() const
ウィンドウの横幅を得る.
Definition: Window.h:1267
GLsizei getHeight() const
ウィンドウの高さを得る.
Definition: Window.h:1274
virtual ~Window()
デストラクタ.
Definition: Window.h:922
GgQuaternion getTrackballData(int button=GLFW_MOUSE_BUTTON_1) const
トラックボールの回転変換行列を得る.
Definition: Window.h:1544
GLfloat getArrowY(int mods=0) const
矢印キーの現在の Y 値を得る.
Definition: Window.h:1359
Window(const char *title="GLFW Window", int width=640, int height=480, int fullscreen=0, GLFWwindow *share=nullptr)
コンストラクタ.
Definition: Window.h:650
GLfloat getControlArrowY() const
CTRL キーを押しながら矢印キーを押したときの現在の Y 値を得る.
Definition: Window.h:1404
GLfloat getWheel(int direction) const
マウスホイールの回転量を得る.
Definition: Window.h:1501
void setClose(int flag=GLFW_TRUE) const
ウィンドウのクローズフラグを設定する.
Definition: Window.h:1184
GLfloat getMouseX() const
マウスカーソルの現在位置の X 座標を得る.
Definition: Window.h:1467
const GLfloat * getLocation(int button=GLFW_MOUSE_BUTTON_1) const
トラックボール処理を考慮したマウスによる現在位置のポインタを得る.
Definition: Window.h:1524
void getAltlArrow(GLfloat *alt_arrow) const
ALT キーを押しながら矢印キーを押したときの現在の値を得る.
Definition: Window.h:1433
void getControlArrow(GLfloat *control_arrow) const
CTRL キーを押しながら矢印キーを押したときの現在の値を得る.
Definition: Window.h:1411
void restoreViewport()
ビューポートをウィンドウ全体に設定する.
Definition: Window.h:1302
GLfloat getMouseY() const
マウスカーソルの現在位置の Y 座標を得る.
Definition: Window.h:1475
GgMatrix getTranslation(int button=GLFW_MOUSE_BUTTON_1) const
トラックボール処理を考慮したマウスによる平行移動の変換行列を得る.
Definition: Window.h:1534
GLfloat getArrowX(int mods=0) const
矢印キーの現在の X 値を得る.
Definition: Window.h:1351
bool getKey(int key)
キーが押されているかどうかを判定する.
Definition: Window.h:1312
GLfloat getAltArrowY() const
ALT キーを押しながら矢印キーを押したときの現在の Y 値を得る.
Definition: Window.h:1426
GLfloat getArrow(int direction=0, int mods=0) const
矢印キーの現在の値を得る.
Definition: Window.h:1342
const GLfloat getWheelX() const
マウスホイールの X 方向の回転量を得る.
Definition: Window.h:1508
const GLfloat getWheelY() const
マウスホイールの Y 方向の回転量を得る.
Definition: Window.h:1515
GLfloat getControlArrowX() const
CTRL キーを押しながら矢印キーを押したときの現在の X 値を得る.
Definition: Window.h:1397
void selectInterface(int no)
インタフェースを選択する
Definition: Window.h:1324
void setKeyboardFunc(void(*func)(const Window *window, int key, int scancode, int action, int mods))
ユーザ定義の keyboard 関数を設定する.
Definition: Window.h:1629
void setUserPointer(void *pointer)
任意のユーザポインタを保存する.
Definition: Window.h:1615
void setMouseFunc(void(*func)(const Window *window, int button, int action, int mods))
ユーザ定義の mouse 関数を設定する.
Definition: Window.h:1636
GLfloat getAltArrowX() const
ALT キーを押しながら矢印キーを押したときの現在の X 値を得る.
Definition: Window.h:1419
GgMatrix getTrackball(int button=GLFW_MOUSE_BUTTON_1) const
トラックボールの回転変換行列を得る.
Definition: Window.h:1554
void swapBuffers()
カラーバッファを入れ替える.
Definition: Window.h:1251
変換行列.
Definition: gg.h:1655
const GLfloat * get() const
変換行列を取り出す.
Definition: gg.h:2414
四元数.
Definition: gg.h:2717
ゲームグラフィックス特論の宿題用補助プログラム GLFW3 版の宣言.
ゲームグラフィックス特論の宿題用補助プログラムの名前空間
Definition: gg.h:1307
GgMatrix ggTranslate(GLfloat x, GLfloat y, GLfloat z, GLfloat w=1.0f)
平行移動の変換行列を返す.
Definition: gg.h:2462
void ggInit()
ゲームグラフィックス特論の都合にもとづく初期化を行う.
Definition: gg.cpp:1319