本項ではファイルから読み込んだ三角形集合の描画を行ってみます。
描画はビューで行うためドキュメントに保持している三角形データを取得する関数を追加して、取得した三角形データを描画するための関数を追加してみましょう。
1. CSTLViewerView クラスで STL データの描画を行うために CSTLViewerDoc に読み込んだデータを取得する関数を用意しておきます。
【STLViewerDoc.h】
class CSTLViewerDoc : public CDocument
{
/* 長くなるので一部省略、ここより上は変更なし */
public:
afx_msg void OnFileOpen();
const CgsTriangles& GetTriangles() const { return m_ctTriangles; }
private:
CgsTriangles m_ctTriangles;
};
2. STLデータを読み込んだ後にビューの再描画を行います。
【STLViewerDoc.cpp】
void CSTLViewerDoc::OnFileOpen()
{
// ファイル選択ダイアログ表示
CString strFilter;
const int length = strFilter.LoadString(IDS_FILTER_STL);
const DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
CFileDialog dlg(TRUE, _T("stl"), NULL, dwFlags, (LPCTSTR)strFilter);
if (dlg.DoModal() != IDOK) {
return;
}
// STLファイル読み込み
CWaitCursor wc;
if (!m_ctTriangles.ReadText((LPCTSTR)dlg.GetPathName())) {
// 読み込みエラー
AfxMessageBox(IDS_ERROR_READ_TEXT);
}
// ビューの再描画
POSITION pos = GetFirstViewPosition();
while (pos) {
CView* pView = GetNextView(pos);
if (pView) {
pView->RedrawWindow();
}
}
}
データ保持に独自クラス (CgsVector, CgsPoint)や色設定に Win32 固有の構造体(COLORREF)を使っています。クラスや構造体に格納した値を OpenGL 関数にそれを渡すには毎回同じようにパラメータ取得関数を記述する必要が出てくるので、簡略化するための関数を用意しておきます。これらの関数は CSTLViewerView クラスのメンバー関数にはしておらず、DrawLine や DrawGL などから呼び出されるので CSTLViewerView が定義される前に記述する必要があります。
【STLViewerView.cpp】
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
inline void glNormal(const CgsVector& v)
{
glNormal3d(v.GetX(), v.GetY(), v.GetZ()); // CgsVector の値を法線ベクトルとして設定
}
inline void glVertex(const CgsPoint& p)
{
glVertex3d(p.GetX(), p.GetY(), p.GetZ()); // CgsPoint の値を頂点として設定
}
inline void glColor(const COLORREF& color)
{
glColor3ub(GetRValue(color), GetGValue(color), GetBValue(color)); // COLORREF の値を色として設定
}
// CSTLViewerView
IMPLEMENT_DYNCREATE(CSTLViewerView, CView)
X軸, Y軸, Z軸の直線描画を簡潔に記述できるようにするための関数を用意します。
1. DrawLine の宣言
【STLViewerView.h】
class CSTLViewerView : public CView
{
/* 長くなるので一部省略、ここより上は変更なし */
private:
HGLRC m_hGLRC;
bool InitGL();
void DrawGL();
bool SetDCPixelFormat(HDC hdc);
void DrawLine(const CgsPoint& sp, const CgsPoint& ep, const COLORREF& color) const;
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
};
2. DrawLine の定義
【STLViewerView.cpp】
void CSTLViewerView::DrawLine(const CgsPoint& sp, const CgsPoint& ep, const COLORREF& color) const
{
glBegin(GL_LINES); // 2頂点を独立した線分として描画開始
glColor(color); // 線色設定
glVertex(sp); // 開始点設定
glVertex(ep); // 終了点設定
glEnd(); // 描画終了
}
STL を描画するための関数を用意します。
1. DrawSTL の宣言
【STLViewerView.h】
class CSTLViewerView : public CView
{
/* 長くなるので一部省略、ここより上は変更なし */
private:
HGLRC m_hGLRC;
bool InitGL();
void DrawGL();
bool SetDCPixelFormat(HDC hdc);
void DrawLine(const CgsPoint& sp, const CgsPoint& ep, const COLORREF& color) const;
bool DrawSTL() const;
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
};
2. DrawSTL の定義
ビューに関連するドキュメントから三角形データを取得し、描画処理を行います。【補足:範囲 for 文】
【STLViewerView.cpp】
bool CSTLViewerView::DrawSTL() const
{
CSTLViewerDoc* pDoc = GetDocument();
if (!pDoc) {
return false;
}
glColor(RGB(192, 192, 192)); // 面色設定
glBegin(GL_TRIANGLES); // 3頂点を独立した三角形として描画開始
const CgsTriangles& triangles = pDoc->GetTriangles();
for (const auto& e : triangles) {
glNormal(e.GetNormal()); // 法線ベクトル設定
for (int i = 0; i < 3; ++i) {
glVertex(e.GetPoint(i)); // 頂点設定
}
}
glEnd(); // 描画終了
return true;
}
軸線描画の処理 は DrawLine 関数に置き換えるので、glBegin(GL_LINES); から glEnd(); までは削除します。
【STLViewerView.cpp】
void CSTLViewerView::DrawGL()
{
// カレントコンテキスト設定
CClientDC dc(this);
wglMakeCurrent(dc.m_hDC, m_hGLRC);
// バッファーのクリア
glClear(GL_COLOR_BUFFER_BIT);
// X軸線描画
glBegin(GL_LINES); // この行から 削除
glColor3d(1.0, 0.0, 0.0); // 削除
glVertex3d(0.0, 0.0, 0.0); // 削除
glVertex3d(100.0, 0.0, 0.0); // 削除
glEnd(); // この行まで 削除
// Y軸線描画
glBegin(GL_LINES); // この行から 削除
glColor3d(0.0, 1.0, 0.0); // 削除
glVertex3d(0.0, 0.0, 0.0); // 削除
glVertex3d(0.0, 100.0, 0.0); // 削除
glEnd(); // この行まで 削除
// Z軸線描画
glBegin(GL_LINES); // この行から 削除
glColor3d(0.0, 0.0, 1.0); // 削除
glVertex3d(0.0, 0.0, 0.0); // 削除
glVertex3d(0.0, 0.0, 100.0); // 削除
glEnd(); // この行まで 削除
// OpenGL関数の実行強制
glFlush();
// オンスクリーンバッファーへのコピー
SwapBuffers(dc.m_hDC);
}
視点等変更の処理を OnSize から此方へ移します。また、軸線描画の処理は DrawLine 関数に置き換え、STL 描画処理を追加します。
【STLViewerView.cpp】
void CSTLViewerView::DrawGL()
{
// カレントコンテキスト設定
CClientDC dc(this);
wglMakeCurrent(dc.m_hDC, m_hGLRC);
// バッファーのクリア
glClear(GL_COLOR_BUFFER_BIT);
// 視点等変更
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(200.0, 200.0, 200.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// X軸線描画
DrawLine({ 0.0, 0.0, 0.0 }, { 100.0, 0.0, 0.0 }, RGB(255, 0, 0));
// Y軸線描画
DrawLine({ 0.0, 0.0, 0.0 }, { 0.0, 100.0, 0.0 }, RGB(0, 255, 0));
// Z軸線描画
DrawLine({ 0.0, 0.0, 0.0 }, { 0.0, 0.0, 100.0 }, RGB(0, 0, 255));
// STL描画
DrawSTL();
// OpenGL関数の実行強制
glFlush();
// オンスクリーンバッファーへのコピー
SwapBuffers(dc.m_hDC);
}
視点等変更の処理を DrawGL へ移したので gluLookAt(200.0, 200.0, 200.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); は削除します。
【STLViewerView.cpp】
void CSTLViewerView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// カレントコンテキスト設定
CClientDC dc(this);
wglMakeCurrent(dc.m_hDC, m_hGLRC);
// 描画領域設定
glViewport(0, 0, cx, cy);
glLoadIdentity();
gluPerspective(30.0, (double)cx / (double)cy, 1.0, 1000.0);
gluLookAt(200.0, 200.0, 200.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // この行は削除
}
また、後続の行列演算をプロジェクションマトリックススタックに変更しておきます。
【STLViewerView.cpp】
void CSTLViewerView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// カレントコンテキスト設定
CClientDC dc(this);
wglMakeCurrent(dc.m_hDC, m_hGLRC);
// 描画領域設定
glViewport(0, 0, cx, cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (double)cx / (double)cy, 1.0, 1000.0);
}
「house.stl」を読み込んでみると、形状のシルエットが見えるようになります。
使用するPCのグラフィックボードやドライバのバージョンなどの環境によっては下図の様に表示されることがあるようです。次項で陰面設定を追加した後は正しく表示されるようになるので、そのまま進めて下さい。