【openTK】STLファイルを表示させたい。

以前取り上げたopenTKのやつ。

これを使ってSTLファイルを表示する、というのをやってみたいと思います。

使用するSTLファイルは、こちらからダウンロードしました。

https://www.3dagogo.com/creativetools/designs/3DBenchy

ただしく処理できれば、こんな風に表示されるはずです。

STLファイルの読み込みはこちらのサイトを参考にしました。

https://codingsquare.net/cs/stlfile/#toc11

STLのデータは法線ベクトルと、三角形を構成する頂点の座標を示す3つのベクトルで構成されています。

法線ベクトルは三角形の面の表側を向いている方向を示すベクトルです。

		public bool ReadBinary(string filePath)
		{
			// filePath が null か、ファイルが存在しない場合はエラーとする
			if (filePath == null || File.Exists(filePath) == false)
				return false;

			try
			{
				// バイナリファイルの読み込み
				using (var reader = new BinaryReader(new FileStream(filePath, FileMode.Open, FileAccess.Read)))
				{
					// ヘッダ読み込み
					Header = reader.ReadBytes(HeaderLength);

					// ファセットの枚数読み込み
					uint size = reader.ReadUInt32();

					// ファイルの残りのバイト数
					long rest = reader.BaseStream.Length - reader.BaseStream.Position;

					// ファセット1枚分のバイト数
					const int FacetLength = 50;

					// ファイルの残りのバイト数が、求められるファセットの枚数分のバイト数より少なければエラー
					if (rest < FacetLength * size)
						return false;

					// 全ファセット読み込み
					Facets = new Facet[size];
					for (int i = 0; i < size; ++i)
					{
						// ファセット1個分のバイト配列読み込み
						byte[] bytes = reader.ReadBytes(FacetLength);

						// ファセットデータ生成と配列への格納
						int index = 0;
						const int offset = sizeof(float);
						Facets[i] = new Facet(
							new Vertex(
								BitConverter.ToSingle(bytes, index),
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset)),
							new Vertex(
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset)),
							new Vertex(
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset)),
							new Vertex(
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset),
								BitConverter.ToSingle(bytes, index += offset))
						);
					}
				}
			}
			catch (Exception)
			{
				return false;
			}
			return true;
		}

これでバイナリのSTLデータを読み込み、描画させます。

        STLFile stlFile = new STLFile();
        public Game() : base(800, 600, GraphicsMode.Default, "0-3:GameWindow")
        {
            stlFile.ReadBinary("3DBenchy.stl");
        }

        //画面描画で実行される。
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.MatrixMode(MatrixMode.Modelview);
            Matrix4 modelview = Matrix4.LookAt(Vector3.Zero, Vector3.UnitZ, Vector3.UnitY);
            GL.LoadMatrix(ref modelview);

            int count = 0;
            foreach(var f in stlFile.Facets)
            {
                count++;
                GL.Begin(BeginMode.Triangles);

                GL.Color4(Color4.White);
                GL.Normal3(f.Normal.X, f.Normal.Y, f.Normal.Z);
                GL.Vertex3(f.Vertex1.X / 30, f.Vertex1.Y / 30, f.Vertex1.Z / 30);
                GL.Vertex3(f.Vertex2.X / 30, f.Vertex2.Y / 30, f.Vertex2.Z / 30);
                GL.Vertex3(f.Vertex3.X / 30, f.Vertex3.Y / 30, f.Vertex3.Z / 30);

                GL.End();
            }

            SwapBuffers();
        }

そしてライトの設定も加えます。

こちらのサイトを参考にしました。

https://ameblo.jp/nishi-u6fa4/entry-10864018960.html

        //ウィンドウのサイズが変更された場合に実行される。
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
            GL.MatrixMode(MatrixMode.Projection);
            Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, (float)Width / (float)Height, 1.0f, 64.0f);
            GL.LoadMatrix(ref projection);
            GL.MatrixMode(MatrixMode.Modelview);

            Matrix4 look = Matrix4.LookAt(3.0f * Vector3.One, Vector3.Zero, Vector3.UnitY);
            GL.LoadMatrix(ref look);
            GL.Enable(EnableCap.Lighting);
            float[] position = new float[] { 1.0f, 2.0f, 3.0f, 0.0f };
            GL.Light(LightName.Light0, LightParameter.Position, position);
            GL.Enable(EnableCap.Light0);
        }

実行結果はこちら。

なんだこりゃ。

たぶん、データは間違ってないと思うんですよ。

問題は視点なのかな、と思います。

視点を変えるとか、変更できるようにすればちゃんと表示させることができるかもしれません。

もうちょっと勉強します。