-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
367 lines (308 loc) · 10 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//
// 地理院地図 3D ビューア
//
// OpenCV の組み込み
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#if defined(_WIN32)
# pragma comment(lib, "ws2_32.lib")
# define _USE_MATH_DEFINES
# include <windows.h>
#endif
// 標準ライブラリ
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <memory>
// ウィンドウ関連の処理
#include "Window.h"
// シェーダ
#define SHADER "simple"
namespace
{
//
// データファイル (Digital Elevation Model) の読み込み
//
GLuint loadDem(const char *filename, GLsizei *count)
{
// データファイルを開く
std::ifstream file(filename);
if (!file) return 0;
// メッシュの分割数
unsigned int slices(0), stacks(0);
// 高さデータの格納先
std::vector<GLfloat> height;
// データの読み込み
std::string line;
while (getline(file, line))
{
std::istringstream sline(line);
++stacks;
// 実数値を一つ読み取る
GLfloat h;
for (slices = 0; sline >> h;)
{
// データを保存する
height.push_back(h);
// 読み込んだ数値の数を数える
++slices;
// コンマ/行末文字を読み飛ばす
char c;
sline >> c;
}
}
// データ数のチェック
if (height.size() < 4 || slices * stacks != height.size()) return 0;
// 縦横の格子点の数ではなく間隔の数にする
--slices;
--stacks;
// データの格納先
std::vector<GLfloat> position, normal;
// 頂点の法線ベクトルを求める
for (unsigned int k = 0, j = 0; j <= stacks; ++j)
{
for (unsigned int i = 0; i <= slices; ++i, ++k)
{
// 処理対象の頂点の周囲の頂点番号
const unsigned int kim = i > 0 ? k - 1 : k;
const unsigned int kip = i < slices ? k + 1 : k;
const unsigned int kjm = j > 0 ? k - slices - 1 : k;
const unsigned int kjp = j < stacks ? k + slices + 1 : k;
// 位置
position.push_back(static_cast<GLfloat>(i) * 2.0f / static_cast<GLfloat>(slices) - 1.0f);
position.push_back(1.0f - static_cast<GLfloat>(j) * 2.0f / static_cast<GLfloat>(stacks));
position.push_back(height[k]);
// 法線
const GLfloat n[] =
{
(height[kim] - height[kip]) / static_cast<GLfloat>(stacks),
(height[kjp] - height[kjm]) / static_cast<GLfloat>(slices),
2.0f / (static_cast<GLfloat>(slices * stacks))
};
// 法線ベクトルを正規化して登録する
const GLfloat l = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
if (l > 0.0f)
{
normal.push_back(n[0] / l);
normal.push_back(n[1] / l);
normal.push_back(n[2] / l);
}
else{
normal.push_back(0.0f);
normal.push_back(0.0f);
normal.push_back(0.0f);
}
}
}
// 頂点のインデックス (面データ)
std::vector<GLuint> index;
// 頂点のインデックスを求める
for (unsigned int j = 0; j < stacks; ++j)
{
for (unsigned int i = 0; i < slices; ++i)
{
const int k((slices + 1) * j + i);
// 上半分の三角形
index.push_back(k);
index.push_back(k + slices + 2);
index.push_back(k + 1);
// 下半分の三角形
index.push_back(k);
index.push_back(k + slices + 1);
index.push_back(k + slices + 2);
}
}
// 頂点配列オブジェクト
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// 高さの頂点バッファオブジェクト
GLuint positionBuffer;
glGenBuffers(1, &positionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, position.size() * sizeof (GLfloat), &position[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
// 法線の頂点バッファオブジェクト
GLuint normalBuffer;
glGenBuffers(1, &normalBuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
glBufferData(GL_ARRAY_BUFFER, normal.size() * sizeof (GLfloat), &normal[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
// 頂点のインデックスバッファオブジェクト
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, index.size() * sizeof (GLuint), &index[0], GL_STATIC_DRAW);
// インデックスの数を返す
*count = static_cast<GLsizei>(index.size());
return vao;
}
}
//
// メインプログラム
//
int main()
{
#if STEREO == OCULUS
// Oculus Rift (LibOVR) を初期化する
if (OVR_FAILURE(ovr_Initialize(nullptr)))
{
// Oculus Rift の初期化に失敗した
#if defined(_WIN32)
MessageBox(nullptr, TEXT("Oculus Rift が初期化できません。"), TEXT("すまんのう"), MB_OK);
#else
std::cerr << "Can't initialize Oculus Rift." << std::endl;
#endif
return EXIT_FAILURE;
}
// プログラム終了時には LibOVR を終了する
atexit(ovr_Shutdown);
#endif
// GLFW を初期化する
if (glfwInit() == GL_FALSE)
{
// GLFW の初期化に失敗した
#if defined(_WIN32)
MessageBox(nullptr, TEXT("GLFW の初期化に失敗しました。"), TEXT("すまんのう"), MB_OK);
#else
std::cerr << "Can't initialize GLFW." << std::endl;
#endif
return EXIT_FAILURE;
}
// プログラム終了時には GLFW を終了する
atexit(glfwTerminate);
// OpenGL ウィンドウの特性
glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
glfwWindowHint(GLFW_STEREO, STEREO == QUADBUFFER);
glfwWindowHint(GLFW_DOUBLEBUFFER, STEREO != OCULUS);
// ディスプレイの情報
GLFWmonitor *monitor;
int window_width, window_height;
// フルスクリーン表示
if (STEREO != NONE && STEREO != OCULUS && !debug)
{
// 接続されているモニタの数を数える
int mcount;
GLFWmonitor **const monitors = glfwGetMonitors(&mcount);
// セカンダリモニタがあればそれを使う
monitor = monitors[mcount > useSecondary ? useSecondary : 0];
// モニタのモードを調べる
const GLFWvidmode *mode(glfwGetVideoMode(monitor));
// ウィンドウのサイズ (フルスクリーン)
window_width = mode->width;
window_height = mode->height;
}
else
{
// プライマリモニタをウィンドウモードで使う
monitor = nullptr;
// ウィンドウのサイズ
window_width = 960;
window_height = 540;
}
// ウィンドウを開く
Window window(window_width, window_height, "STER Display", monitor);
if (!window.get())
{
// ウィンドウが作成できなかった
#if defined(_WIN32)
MessageBox(nullptr, TEXT("GLFW のウィンドウが開けませんでした。"), TEXT("すまんのう"), MB_OK);
#else
std::cerr << "Can't open GLFW window." << std::endl;
#endif
return EXIT_FAILURE;
}
// 描画用のシェーダプログラムを読み込む
GgSimpleShader shader(SHADER ".vert", SHADER ".frag");
if (!shader.get())
{
// シェーダが読み込めなかった
#if defined(_WIN32)
MessageBox(nullptr, TEXT("シェーダファイルの読み込みに失敗しました。"), TEXT("すまんのう"), MB_OK);
#else
std::cerr << "Can't read shader file: " SHADER ".vert, " SHADER ".frag" << demfile << std::endl;
#endif
return EXIT_FAILURE;
}
// 地形に貼り付けるテクスチャのサンプラの場所を得る
GLint cmapLoc(glGetUniformLocation(shader.get(), "cmap"));
// 地形データを読み込む
GLsizei count;
const GLuint mesh(loadDem(demfile, &count));
if (mesh == 0)
{
// 地形データが読み込めなかった
#if defined(_WIN32)
MessageBox(nullptr, TEXT("データファイルの読み込みに失敗しました。"), TEXT("すまんのう"), MB_OK);
#else
std::cerr << "Can't read data file: " << demfile << std::endl;
#endif
return EXIT_FAILURE;
}
// 読み込んだ地形データを表示する際のスケール
const GgMatrix mm(ggScale(demscale));
// 地形に貼り付けるテクスチャ
GLuint tex(0);
// 地形に貼り付けるテクスチャの画像を読み込む
cv::Mat src(cv::imread(texfile));
if (src.data)
{
// テクスチャに読み込んだ画像を転送する
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, src.cols, src.rows, 0,
GL_BGR, GL_UNSIGNED_BYTE, src.data);
// ミップマップを作成して有効にする
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// 境界色は黒にしておく (これは Oculus Rift への表示時に表示範囲外の色になる)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
}
#if USE_ANISOTROPIC_FILTERING
// 非対象フィルタリング拡張機能を有効にする
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
#endif
// 隠面消去処理を有効にする
glEnable(GL_DEPTH_TEST);
// 背面はカリングしない
glDisable(GL_CULL_FACE);
// 背景色を設定する
glClearColor(back[0], back[1], back[2], back[3]);
// ウィンドウが開いている間くり返し描画する
while (!window.shouldClose())
{
// 画面クリア
window.clear();
for (int eye = 0; eye < (STEREO == NONE ? 1 : 2); ++eye)
{
// 描画領域を選択する
window.select(eye);
// 描画用のシェーダプログラムの使用開始
shader.use();
shader.setLight(light);
shader.setMaterial(material);
glUniform1i(cmapLoc, 0);
// テクスチャの指定
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
// 左目のモデルビュープロジェクション変換行列を設定する
shader.loadMatrix(window.getMp(eye), window.getMw(eye) * mm);
// 図形データの指定
glBindVertexArray(mesh);
// 描画
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0);
}
// バッファを入れ替える
window.swapBuffers();
}
}