【本サイトのご利用指針】
当サイトはSPAM対策等のためJavaScriptを使っています。
JavaScriptの実行を可能な状態にしてご利用下さい。
Please enable the execution of JavaScript!




Vocaloid初音ミクMikuMikuDanceという驚愕のソフトウェア

  【リンクフリー】 私設研究所ネオテックラボ Neo-Tech-Lab.co.uk
【記載者】 私設研究所Neo-Tech-Lab 上田智章

 
ここにチェックボックス型外部コンテンツ・メニューが入ります。




【ブラウザはGoogle Chrome】

●最新版ダウンロードはこちら

【お知らせ】

現在、特にお知らせする事項はありません。

【メニュー】
[index] ■メモ
[PMD1] ●【VBA】ポリゴン・フィルとテクスチャー・マッピング
[PMD2] ●【VBA】透視変換
[PMD3] ●【VBA】光源計算
[Sensor]★【NyARToolkitCS】拡張現実センサ【ソース公開】C#
[PMD6] ■【NyARToolkitCS】MMDのPMDで拡張現実(モデル描画)C#

[PMD4] ■【NyARToolkitCS】MMDのPMDで拡張現実(表情処理)C#
[PMD5] ●IK bone制御

[Kinect]★【Kinect】デプスカメラを自作してみた【原理説明あり】
[Kinect]★【Kinect】Kinect for Windows SDK導入編【C#サンプルあり】
[Kinect]★【Kinect】OpenNI導入編【C#サンプルあり】

[TTS]  ★【iSpeech APIの音声合成TTS】【ソース公開】JavaScript
[TTS]  ★『JavaScriptだけで音声合成エンジンμ-iVoiceを製作してみた』



■修正日2014年12月31日■06:10頃記載

【奈緒(にゃお)プロジェクト】

Kinect V1/V2, Leap Motion, RealSense等のデバイスを使った研究(UIやロボット、Agent等)、雑誌投稿、学会及び展示会などでの成果発表、個人の動画作成利用などの趣味を支援するために、著作権フリーのオリジナル3Dモデル(MikuMikuDanceのPMDフォーマット)を製作する私的プロジェクトを開始しました。現在、たかはる氏によるキャラクタ・デザイン・フェーズから衣装デザインフェーズに移行しつつある状態です。
他に並行して私的ロボットプロジェクトもスタートしました。将来的にこの2つのプロジェクトを融合する予定です。

【奈緒のプロフィール】

猫耳、巨乳、ナースタイプのロボット。身長:159cm。しっぽの先からは回路の信号を取得したり、送り込める光る触手状プローブが伸ばすことができる。

【現行利用規定案】

雑誌記事、研究成果の展示利用に関して申請不要。他の商用利用はペンディング。趣味などの私的利用や研究利用は著作権フリー。

【キャラクタデザイン作画】

  (奈緒)たかはる氏  (にゃお)えゐみーさん


【WebGLのページ】

JavaScriptでリアルタイム3次元グラフィックスが楽しめる時代の到来までもうすぐです。
現在、Google Chrome12、Mozilla FireFox5、及び開発版のSafari、Operaで使うことができます。
WebGLは、JavaScriptエンジンv8とDirectXを利用して高速化したOpenGLを融合して構成されたものです。
2011年5月に指摘されたセキュリティー問題はありますが、処理速度の高速性から利便性も高いので、将来的には普及するのではないでしょうか?
 ●WebGLとは? ●事前準備:利用できるウェブブラウザは? ●事前準備:FireFox5でWebGLが動作しない場合には?
 ●WebGLのサンプル ●WebGLのデモ ●クロスドメインテクスチャーを将来も利用できる方法
 ●ローカルのPMDファイルを読み込んでJSON形式に変換し、3次元モデルを表示
 ●物理エンジンBullet.jsのWebGLデモの紹介(Pl4n3氏のragdollデモの修正版)[btBoxShapeとbtCapsuleShape]

【MMD on WebGL】

現時点で、edvakf氏のMMD on WebGLがHTML5+JavaScript(WebGL)で3次元モデル表示をモーション付きで実装されている最も優れたコードだと思います。PMD形式のモデルファイルとVMD形式のモーションファイルをバイナリで読み込み実行することができます。使い方も簡単です。
1) MMDデモ1(背景の手前に表示)
2) MMDデモ2(Webカメラを同時に使う場合)
3) MMDデモ3(YouTube動画の上に表示する場合)
4) MMD.jsのソース(若干コメントを付与したもの。)



【Canvasで3次元グラフィックス】

NTL3D.jsを使えば、HTML5のCanvasを使って簡単に3次元グラフィックスを描画することができます。
まずは、
サンプルプログラムを実行してみてください。右のようなテクスチャーマッピングを表示します。JavaScriptで記述されていますのでブラウザの『ソースを表示』でソースを見ることができます。他の3次元グラフィックスと同様に、3次元モデルは頂点座標(X,Y,Z)とテクスチャーUV座標(U,V)からなる頂点リスト(Vertex)と、どの頂点番号でポリゴン(三角形)を構成するかを示す頂点番号インデックスリストで構成されており、Ball.jsにデータが記述されています。
NTL3D.jsは全てソフトウェアで描画を実行しているので、Canvasが利用可能なブラウザであれば利用することが可能です。Google Chrome, FireFox, IE11, Opera, Safariだけでなく、XP, VistaのIE9でも表示することが可能です。
  CQ出版株式会社インターフェース2013年9月号の記事『NTL3D.js:Canvasで3次元グラフィックス』の筆者個人サイト内サポートページに飛びます。

【サンプルプログラム】/Canvas/NTL3D_TextureMapping3.htmの実行結果

【サンプルプログラム】『回転するピラミッド』/Canvas/NTL3D_Pyramid.htm

【今後の予定】

亀足で、透視変換、光源計算、ボーン制御等を盛り込んでいく予定です。

【μ-iVoice】『JavaScript版 Web音声合成(シンセサイザ/ボーカロイド機能付)API』

JavaScriptだけで実現したNeo-Tech-Lab特製の音声合成エンジンAPIです。
本来ハードウェア向けのDirect Digital Synthesizer方式を採用しています。
シンセサイザやボーカロイド風歌唱も可能です。ピアノロールではなく、テキスト読み上げ方式(Text-To-Speech)です。文字数の制約はありません。⇒【Version0.09C】
漢字⇒かな変換辞書、抑揚制御スクリプトも装備しています。通常はテキスト入力して再生ボタンをクリックするだけです。ただ、漢字辞書はまだあまり充実していないので、全角文字で、ひらがなとカタカナを使って入力してください。
モノラル, 16bit, 22.05kHzで0.5秒スパン135音の音データに基づいて、任意話者での発声を実現しています。(自分の声はいくらエディットしてもなんか癖を感じ、やっぱりキモイのでアップしませんが。)但し、ソースは子音等の扱いに関して処理をはしょったレンダリング速度最優先の音質劣化バージョンとさせていただいています。是非、本家Ⅱのレンダリング速度とJavaScript DDS方式を比べてみて下さい。

【対象ブラウザ】 Google Chrome, Mozilla FireFox, Apple Safari
 ●『なんちゃってミク』NTL版Text-To-Speech API [JavaScript Ver.0.06]
   ちょっとロボティックな『なんちゃってミク』声での短い挨拶テスト
 ●『なんちゃってミク』Ieavan Polkka [JavaScript Ver.0.06]
   ブラウザで波形生成(rendering)を行うので音が出るまでに10~30秒を要します。
   気長にお待ちください。 (スタンダード・ドラム・セット付)
 ●テキスト入力で音声合成できるμ-iVoice体験版Ver.0.09Cを公開中です。⇒

【体験版デモページ】Version0.09C

JavaScriptなので『ソースを表示』でソースを閲覧できます。
初回のアクセスは少しお待ちいただく必要があります。
スタートまでの時間がかかるのは、90秒分のメッセージと2分21秒分のシンセサイザー演奏デモのレンダリングを行っているためです。デモの初期化部分を外せば短時間で立ち上がります。
  ■【sites.google.com】【埋め込みμ-iVoice】
  ■【web.fc2.com】【μ-iVoice】
  ■【geocities.jp】【μ-iVoice】


MMD(MikuMikuDance)のモデル・ビュワー
  1) 初音ミク用Excel VBA
  2) 亞北ネル用Excel VBA
  3) 鏡音リン用Excel VBA
  4) 弱音ハク用Excel VBA
  5) 咲音メイコ用Excel VBA
  6) 鏡音レン用Excel VBA
  7) カイト用Excel VBA

【Bone ,IK Boneに関して】

■Excel VBAのソース内にこのソースでは記述していたものの使用していなかった部分ですが、ミスを発見しました。Visual C#のソースになりますが、『【NyARToolkitCS】MMDのPMDで拡張現実(表情処理)』に記載しました。ご参考まで。
 要点はBoneブロック内にIK接続情報(short型)があったこと、IK Boneブロック内にIK_chain_lengthが0のときは配列IK_child_bone_indexは存在しないことの2点でした。詳しくはソースをご覧ください。

【バグ修正のお知らせ】

■バグを発見したので、NTL_Lib3DCG_ver048.zipをお使い下さい。
詳しくはメモを書きましたのでこちらをご覧ください。
■記載日2009年9月29日■

【MikuMikuDanceのモデル(PMD)をExcel VBAで表示するぞ!】

Google O3Dプラグインを用いてWeb上で初音ミクを表示させ、喋らせてみようと大それた事を考えている。
勿論、現時点でO3Dは3Dグラフィックスのみでオーディオ出力はできないので、Flash Playerを併用するつもりだ。いや仕様的にはWindows Media PlayerかSilverLightになってしまう可能性も大だが
(2011年7月現在、WebGLが利用可能なブラウザが増えてきたので、O3Dをやめて、WebGLに変更。音声合成も当時は知らなかったBase64エンコーディングという方法を見出したので、JavaScriptベースのDirect Digital Synthesizer方式に決定しました。上欄に途中結果を示しています。)
そこでそのための一歩として、MikuMikuDanceのモデルデータ(PMD形式)の移植を行ってみようと考えた。 しかし、PMDデータを1から解析してO3Dで利用できる書式に変更するのは大変だ。そこで、偶然、他の事を検索していて見つけた『通りすがりの記憶』を参考にさせていただいた。いやぁ、このページが存在しなければ、私の勝手な思い込みのため決してデータの抽出はできなかっただろう。感謝。感謝。(^_^;)
左側に表示しているのはPMDから抽出した咲音メイコの形状データをワイヤフレーム表示や3次元グラフィックスで表示したものだ。以前に作ったことのある3次元グラフィックス・ライブラリを大幅改訂して、できる限りDirectXとMMD(MikuMikuDance)の構造体をそのまま利用できる形態のライブラリをMicrosoft社のExcel VBA(Visual Basic for Application)で製作した。
え?DirectXをどうして使わないのかって?
確かにExcel VBAだとハードウェアの動作速度の1/100以下の速度しか出ないだろうし、リアルタイム性は全くない。でもアルゴリズムが丸見えなのだ。
Excel Sheetなら手動でもデータの書き換えが簡単だし、将来的にFPGAで組み込みマイコン向けに3次元グラフィック・アクセラレータを設計するためのアルゴリズム評価を行うことができる。
(まぁ、そんなのは言いわけだ。えぇ、そうですよ。DirectXが使えないだけなんだよ! orz....)
でも、O3DMikuMikuDanceも描画段階ではDirectXを使用しているので、できるだけDirectXのデータ構造に近い記述を試みてみた。

    【メニュー】
     MikuMikuDanceのフォルダとファイル
     PMD形式ファイルのデータ構造
     PMDデータをExcel Sheetへ読出す
     Excelでも行える3次元グラフィックス表示
     3次元グラフィックス・ライブラリ[Lib_3DGraphic.bas]

【今後の予定】
 MMD(MikuMikuDance)のモデルで実際に3次元グラフィックスを見ながら確認できるようになったのでアルゴリズムを詰めていきたい。遠大な計画になりそう。大体こんな感じかな?

 透視変換光源計算(平行光源)PMDで拡張現実表情 ⇒ Bone ⇒ IK Bone ⇒
  (透視変換まで完了 [2009/10/2])   (光源計算まで完了 [2009/10/3])
 (Altera社のFPGAで超高速グラフィック・アクセラレータを新規に設計して動かすのも面白いかも。)
 

【注意】
 1) 左のビュワーには元データである.pmdやテクスチャー.bmp等は添付していません。
プログラムでは'C:\MyWeb\MMD\'フォルダ内にデータが存在することを前提にしています。
 2) 現時点で、テクスチャーデータは実際のデータをペイント等を使って上下を反転させたものを使う必要があります。
 3) 左Excel VBAでソースが閲覧できます。Win32API.basは使っていませんので、削除してOkです。

【NyARToolkitCSを使って拡張現実】


【追記】MikuMikuDanceのPMDファイルを直接読み込んでNyARToolkitCSによる拡張現実を体験するプログラムを製作中です。ソース公開中です。
 標準モデルの『Sphere Mapping』には対応しましたが、テクスチャとの合成やspa等の仕様には未対応です。


 表情処理の実装確認終わりました。テスト動画やプロジェクトファイルも公開中です。
 現在はボーン処理と温度センサのデモ・プログラムをカメ足製作中です。でもなかなかうまく行きませんね.......。

   


図1 MikuMikuDance_vxxxフォルダ

図2 Dataフォルダ内

図3 UserFileフォルダ内

MMD(MikuMikuDance)のフォルダとファイル


 MMD(MikuMikuDance)をダウンロードすると、図1に示すようにMikuMikuDance_vxxx(xxxにはversion番号が入る。)というフォルダが作られ、その下にトゥーンtoonxx.bmp(xxには数字が入る)ファイルが格納されたDataフォルダと、UserFileというフォルダが作成される。 UserFileフォルダ内にはさらに幾つかのサブフォルダが作られている。
 図2に示すように、Dataフォルダ内にはtoonxx.bmpのほか、メニューで使うアイコンなどの共通データが格納されている。
 図3に示すように、UserFileフォルダ内にはDirectXフォーマットで製作されたアクセサリを格納するAccessoryフォルダと、背景にする壁紙を入れるBackGroundフォルダ、PMDデータ形式で記述されたMMDモデルデータと眼等のテクスチャーファイルが格納されるModelフォルダ、モーションデータが格納されるMotionフォルダ、手の形やちょっとしたポーズを格納するPoseフォルダ、Vocaloidのボーカルデータ(.vsq形式)が格納されるvsqフォルダ、音楽データ(.wav形式)が格納されるWaveフォルダがサブフォルダとして存在している。
 図4に示すように、Modelフォルダ内には、モデルごとの定義データであるPMDデータ(.pmd形式)やダミーボーン、眼や咲音メイコのテクスチャーファイル、最近追加されたSphere Mappingで用いるsph形式ファイル(実はファイル構造はbmpファイルと同じ。)が格納されている。
このようにモデルの形状やデザインを決定しているファイルはDataフォルダとModelフォルダに記録されている。重要なのはいうまでもなく、PMDデータである。




図4 Modelフォルダ内

PMD形式ファイルのデータ構造


 PMDデータはMMD(MikuMikuDance)特有のデータ構造である。描画段階ではDirectXが使用されているが、光源計算、IK Bone、物理エンジンBulletの剛体/ジョイントデータ、Sphere Mappingなどの機能を扱い易く盛り込むために独自の工夫がなされたデータ構造となっている。
 描画段階ではDirectXを使うので、3次元座標やベクトルを示すx_Vector型、テクスチャーマッピングを行うためのuv座標を示すx_Coords2d型、透過率を含めた物体色を示すx_ColorRGBA型、他の光源計算での反射率等を示すためのx_ColorRGB型、物体ごとの色を決定するために重要な材質(material)を示すx_material型についてはDirectXに準拠している。(厳密にはmaterial型については互換性はない。DirectXではmaterial型にテクスチャーファイルを示す項が付与される場合があるためだ。)
私はDirectXについて全く素人なので、MikuMikuDanceの標準添付アクセサリを素材としてそのデータ構造を調べてみた。
MikuMikuDanceに添付されているDirectXのデータはメモ帳で開くことができる。そうなのだ。テキスト・データなのだ。 取り敢えず、ステージデータ(stage01.x)をメモ帳で開いてみると、最初にデータ構造を記述するtemplateという部分が書かれている。 今回重要な部分だけを記載したが、座標変換用マトリックスや他の仕様もデータ構造がわかれば理解しやすい。

 他の表示デバイスにモデルを移植するためにはPMD特有のデータ構造のうち太字の2つ型は特に重要だ。PMD_vertex型とPMD_material型はエッジ・ハイライティングやトゥーン、Bone操作に伴う変形率、光源計算などを行い易くするための検討がなされた独特のデータ構造を持っている。
 PMDファイルのうちモデルの表示を行う上で重要なデータブロックは、PMD_Header型、PMD_VertexList型、PMD_FaceList型、PMD_MaterialList型の4つである。
 PMD_Header型は、著作権を示すためのヘッダー部である。
 PMD_VertexList型は、頂点データ(x,y,z)を格納するために用いられるDirectXのVertex Bufferに相等するが、法線ベクトル(nx,ny,nz)やテクスチャーマッピング(u,v)なども格納している。
 PMD_FaceList型は、DirectXのIndex Bufferに相等する。MikuMikuDanceのモデルではMeshデータはすべて三角形で定義されており、各三角形(面)ごとの頂点番号を3個一組で順に記録している。
 PMD_MaterialList型は、通常の材質(material)以外にトゥーン番号やテクスチャーマッピングを行う場合の参照ファイル名等の情報を持っている。
 以下に、Microsoft社ExcelのVBA(Visual Basic for Application)での定義例を示す。(太字以外の部分は検証していないので注意が必要。)

ここに簡単にポリゴン・フィルとテクスチャー・マッピングのアルゴリズムを示す。




' *************************************
' *****【DirectX の 基本データ型】*****
' *************************************
' DirectXで使用される基本データ型を定義します。本ビューワーで使うのは以下の5つのタイプです。
' 1) x_Vectorは3次元の頂点座標やベクトルを定義するために使います。
' 2) x_Coords2dは主としてテクスチャーマッピングで使うuv座標を定義するために使います。
' 3) x_ColorRGBAは物体色を定義するために使います。R,G,B,A共に範囲は0.0~1.0です。A(alpha)は透過率を示します。
' 4) x_ColorRGBは鏡面反射係数や散乱光係数を定義するために用います。R,G,B共に範囲は0.0~1.0です。
' 5) x_materialは物体の光源計算に必要な物体色、鏡面反射係数、反射強度、散乱光係数を定義する材質を示します。

Public Type x_Vector '【頂点座標、ベクトル】
  x As Single ' x座標(単精度浮動小数点形式4Bytes)
  y As Single ' y座標(単精度浮動小数点形式4Bytes)
  z As Single ' z座標(単精度浮動小数点形式4Bytes)
End Type

Public Type x_Coords2d '【テクスチャーマッピング用UV座標】
  u As Single ' u座標(単精度浮動小数点形式4Bytes)
  v As Single ' v座標(単精度浮動小数点形式4Bytes)
End Type

Public Type x_ColorRGBA '【物体色】
  Red As Single ' 赤(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Green As Single ' 緑(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Blue As Single ' 青(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Alpha As Single ' 透過率(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
End Type

Public Type x_ColorRGB '【鏡面反射色、散乱光色】
  Red As Single ' 赤(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Green As Single ' 緑(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Blue As Single ' 青(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
End Type

Public Type x_Material '【材質定義】
  FaceColor As x_ColorRGBA ' 物体色
  Power As Single ' 反射強度(単精度浮動小数点形式4Bytes)
  SpecularColor As x_ColorRGB ' 鏡面反射色
  EmissiveColor As x_ColorRGB ' 散乱光色
End Type


'*************************************************************************
'**********************【MikuMikuDance独自の構造体】**********************
'*************************************************************************
' MikuMikuDanceで使われている独自の構造体をまとめました。
' このプログラムでは最初の2タイプを使用しています。
' なお、MikuMikuDance独自の構造体及び、PMDファイルの構造についての詳細はこちらのページ(通りすがりの記憶)のお世話になりました。
Public Type PMD_vertex '【PMD_頂点データ】 4*3+4*3+4*2+2*2+2 = 12+12+8+4+2 = 38bytes/頂点
  pos As x_Vector '・【頂点座標】x,y,z
  normal As x_Vector '・【法線ベクトル】nx,ny,nz
  uv As x_Coords2d '・【uv座標】MMDは頂点uv(範囲0.0-1.0) ◆読込時に範囲u:0-(cg.Header.Nx-1),範囲v:0-(cg.Header.Ny-1)に変換する。
  bone_num(1) As Integer '・【Bone番号】Bone番号1, Bone番号2 モデル変形(頂点移動)時に影響
  bone_weight As Byte '・【Bone重み係数】Bone1に与える影響度 0-100 Bone2への影響度は(100-bone_weight)で与えられる。
  edge_flag As Byte '・【エッジ・フラグ】0:通常、1:エッジ無効 輪郭線が有効の場合
End Type '

Public Type PMD_material '【PMD_材質データ】
  material As x_Material '・【材質データ】DirectXの材質データ構造体と同じ
  toon_index As Byte '・【トゥーン・インデックス番号】toon??.bmp 0.bmp:0xFF, 1.bmp:0x00, ・・・10.bmp:0x09
  edge_flag As Byte '・【エッジ・フラグ】輪郭、影
  face_vert_count As Long '・【面頂点数】面数*3 = 面頂点数 実際のindexに直すには材質0から累積加算する。
  texture_file_name As String * 20 '・【テクスチャ・ファイル名】20bytesギリギリまで使える 20bytes時は0x00がなくても可。'
End Type

Public Type PMD_bone '【PMD_Boneデータ】
  bone_name As String * 20 '・【Bone名】Boneの名称
  parent_bone_index As Integer '・【親Bone番号】(無い場合は0xFFFF)
  tail_pos_bone_index As Integer '・【tail位置のBone番号】(chain末端は0xFFFF) 親:子は1:多なので位置決め用
  bone_type As Byte '・【Boneの種類】0:回転のみ 1:回転と移動 2:IK 3:不明 4:IK影響下 5:回転影響下 6:IK接続先 7:非表示
  dummy As Integer '・【IKBone関連?】詳細不明            ↑注) Boneの種類(MMD 4.0~) 8:捻り 9:回転運動
  bone_head_pos As x_Vector '・【Boneのヘッド座標】x,y,z
End Type

Public Type PMD_IK_data '【PMD_IK_data】
  IK_bone_index As Integer ' IK Bone番号
  IK_target_bone_index As Integer ' IKターゲットBone番号 IK Boneが最初に接続するBone
  IK_chain_length As Byte ' IKチェーンの長さ(子の数)
  Iterations As Integer ' 再帰演算回数 IK値1
  Control_weight As Single ' IKの影響度 IK値2
  Ik_child_bone_index() As Integer ' 配列サイズはIK_chain_length IK影響下のBone番号
End Type

Public Type PMD_Skin_vert_data '【PMD_Skin_vert_data】 (type:base) | (type:base以外)
  Skin_vert_index As Long ' 表情用の頂点の番号(頂点リストにある番号) | 表情用の頂点の番号(baseの番号。skin_vert_index)
  Skin_vert_pos As x_Vector ' x,y,z 表情用頂点座標(頂点自体の座標) | x,y,z 表情用頂点の座標オフセット値(baseに対するオフセット)
End Type

Public Type PMD_Skin_data '【PMD_Skin_data】
  Skin_name As String * 20 ' char skin_name[20] 表情名
  Skin_vert_count As Long ' 表情用頂点数
  Skin_type As Byte ' 表情の種類 0:base, 1:まゆ, 2:眼, 3:リップ, 4:その他
  Skin_vert_data() As PMD_Skin_vert_data '配列サイズはSkin_vert_count 表情用の頂点データ(16bytes/頂点)
End Type

Public Type PMD_Bone_disp '【PMD_Bone_disp】
  Bone_index As Integer 'WORD bone_index; // 枠用Bone番号
  Bone_disp_frame_index As Byte 'BYTE bone_disp_frame_index; // 表示枠番号
End Type

Public Type PMD_RigidBody '【PMD_RigidBody】
  RigidBody_name As String * 20 ' 剛体名 // 頭
  RigidBody_rel_bone_index As Integer ' 剛体関連ボーン番号 // 03 00 == 3 // 頭
  RigidBody_group_index As Byte ' 剛体グループ番号 // 00
  RigidBody_group_target As Integer ' 剛体グループ:対象 // 0xFFFFとの差 // 38 FE
  Shape_type As Byte ' 剛体形状:タイプ(0:球、1:箱、2:カプセル) // 00 // 球
  Shape_w As Single ' 剛体形状:半径(幅) // CD CC CC 3F // 1.6
  Shape_h As Single ' 剛体形状:高さ // CD CC CC 3D // 0.1
  Shape_d As Single ' 剛体形状:奥行 // CD CC CC 3D // 0.1
  Pos_pos As x_Vector ' 位置:位置(x, y, z)
  Pos_rot As x_Vector ' 位置:回転(rad(x), rad(y), rad(z))
  RigidBody_weight As Single ' 剛体:質量 // 00 00 80 3F // 1.0
  RigidBody_pos_dim As Single ' 剛体:移動減 // 00 00 00 00
  RigidBody_rot_dim As Single ' 剛体:回転減 // 00 00 00 00
  RigidBody_recoil As Single ' 剛体:反発力 // 00 00 00 00
  RigidBody_friction As Single ' 剛体:摩擦力 // 00 00 00 00
  RigidBody_type As Byte ' 剛体:タイプ(0:Bone追従、1:物理演算、2:物理演算(Bone位置合せ)) // 00 // Bone追従
End Type

Public Type PMD_Joint '【t_joint】
  Joint_name As String * 20 ' ジョイント名称 // 右髪1
  Joint_rigidbody_a As Long ' ジョイント:剛体A
  Joint_rigidbody_b As Long ' ジョイント:剛体B
  Joint_pos As x_Vector ' ジョイント:位置(x, y, z) // 諸データ:位置合せでも設定可
  Joint_rot As x_Vector ' ジョイント:回転(rad(x), rad(y), rad(z))
  Constrain_pos_1 As x_Vector ' 制限:移動1(x, y, z)
  Constrain_pos_2 As x_Vector ' 制限:移動2(x, y, z)
  Constrain_rot_1 As x_Vector ' 制限:回転1(rad(x), rad(y), rad(z))
  Constrain_rot_2 As x_Vector ' 制限:回転2(rad(x), rad(y), rad(z))
  Spring_pos As x_Vector ' ばね:移動(x, y, z)
  Spring_rot As x_Vector ' ばね:回転(rad(x), rad(y), rad(z))
End Type


'*********************************************************************
'**********************【PMDファイルの構成要素】**********************
'*********************************************************************
' 実際のPMDファイルの構造を示します。本プログラムでは太字部分の4つのブロックしか使っていません。
Public Type PMD_Header '【ヘッダー】
  ID As String * 3 ' char magic[3]; // "Pmd"
  Version As Single ' float version[4]; // 0x00 0x00 0x80 0x3F == 1.00
  ModelName As String * 20 ' char model_name[20]; // モデル名称
  Comment As String * 256 ' char comment[256]; // コメント欄
End Type ' 注) 文字列:終端0x00 パディング0xFD 文字コード:shift JIS

Public Type PMD_VertexList '【頂点リスト】一般にVertexリストと呼ばれています。
  vert_count As Long ' DWORD vert_count; // 頂点数
  vertex() As PMD_vertex ' PMD_vertex vertex[vert_count]; // 頂点データ(38bytes/頂点)
End Type

Public Type PMD_FaceList '【面リスト】MikuMikuDanceのモデルではtriangle(三角形)しか使っていません。
  face_vert_count As Long ' DWORD face_vert_count; // 面数(三角形の数)*3 この配列の要素数
  face_vert_index() As Integer ' WORD face_vert_index[face_vert_count]; // 頂点番号(3個/面)
End Type ' 注)この配列のためにMMDでは最大65535頂点までのモデルしか取り扱えない。

Public Type PMD_MaterialList '【材質リスト】material
  material_count As Long ' DWORD material_count; // 材質数
  material() As PMD_material ' t_material material[material_count]; // 材質データ(70Bytes/material)
End Type

Public Type PMD_BoneList '【Boneリスト】
  bone_count As Integer ' WORD bone_count; // Bone数
  Bone() As PMD_bone ' t_bone bone[bone_count]; // Boneデータ
End Type

Public Type PMD_IK_List '【IKリスト】
  IK_data_count As Integer ' WORD ik_data_count; // IKデータ数
  IK_data() As PMD_IK_data ' PMD_ik_data ik_data[ik_data_count]; // IKデータ (11 + 2*ik_chain_length)/IK
End Type

Public Type PMD_SkinList '【表情リスト】
  Skin_count As Integer ' WORD skin_count; // 表情数
  Skin_data() As PMD_Skin_data 't_skin_data skin_data[skin_count]; // 表情データ
End Type

Public Type PMD_SkinDispList '【表情枠用表示リスト】
  Skin_dsip_count As Byte 'BYTE skin_disp_count; // 表情枠に表示する表情数
  Skin_index() As Integer 'WORD skin_index[skin_disp_count]; // 表情番号
End Type

Public Type PMD_BoneDispNameList '【Bone枠用枠名リスト】
  Bone_disp_name_count As Byte 'BYTE bone_disp_name_count; // Bone枠用の枠名数
  Disp_name As String * 50 'char disp_name[50][bone_disp_name_count]; // 枠名(50Bytes/枠)
End Type

Public Type PMD_BoneDispList '【Bone枠用表示リスト】
  Bone_disp_count As Long 'DWORD bone_disp_count; // Bone枠に表示するBone数(枠0センターを除く全てのBoneの合計)
  Bone_disp() As PMD_Bone_disp ' bone_disp[bone_disp_count]; // 枠用Boneデータ(3Bytes/枠)
End Type
'注)bone_disp_countの部分は
'BYTE bone_disp_count + 0x0000(Bone0) + 0x00(枠0)
'のように見えますが、DWORDです。
'*BYTEにすると総数が合わない(0x000000をデータとしてカウントすると1足らない)
'*Bone番号がWORDなので、WORD以上のサイズが必要
'*データの区切りの位置から判断すると、WORDにはならない。

'**********************【PMD拡張部分】*********************
Public Type PMD_Header_eg '【英語メニュー表示拡張】
  English_name_compatibility As Byte 'フラグ 01:英名対応あり
  Model_name As String * 20 '【英語ヘッダー】モデル名model_name_eg[20]
  Comment As String * 256 '     comment_eg[256]; // コメント(英語)
  Bone_name_eg() As String * 20 '【Boneリスト】char bone_name_eg[20][bone_count]; // Bone名(英名)
  Skin_name_eg() As String * 20 '【表情リスト】char skin_name_eg[20][skin_count-1]; // 表情名(英名) baseは英名が登録されないため
  Disp_name_eg() As String * 50 '【Bone枠用枠名リスト】char disp_name_eg[50][bone_disp_name_count]; // 枠名(英名) MMDでは区分名 Centerは英名は登録されない。
End Type

'********************【トゥーンテクスチャ】********************
Public Type PMD_ToonTextureList '【トゥーンテクスチャリスト】(Toon指定)
  toon_filename(9) As String * 100 'トゥーンテクスチャファイル名 toon_file_name[100][10]
End Type

'********************【Bullet物理演算】********************
Public Type PMD_RigidBodyList '【物理演算_剛体リスト】
  RigidBody_count As Long 'DWORD rigidbody_count; // 剛体数 // 2D 00 00 00 == 45
  RigidBody() As PMD_RigidBody 't_rigidbody[rigidbody_count]; // 剛体データ(83Bytes/rigidbody)
End Type

Public Type PMD_JointList '【物理演算_ジョイントリスト】 0x0007 3111~0x0007 3E28(ファイル末尾)
  Joint_count As Long 'DWORD joint_count; // ジョイント数 // 1B 00 00 00 == 27
  Joint() As PMD_Joint 't_joint[joint_count]; // ジョイントデータ(124Bytes/joint)
End Type

'【剛体について】
'【補足1】
'pos_rot[3]; // 位置:回転
'記録される値は、設定ボックスの値(度)をラジアンに変換した値(若干の誤差あり)
'※1 記録される値 = 設定ボックスの値 * Pi / 180
'※2 PI == 3.1415920
'※3 有効桁数は8桁
'※4 設定ボックスに表示されていない桁も計算対象とするようです。(値をペーストした場合など)
'※5 記録される値は2*PI(360度)で制限されません
'【補足2】
'rigidbody_group_target; // 諸データ:グループ:対象
'・各値を(設定値-1)左シフトした後、ビットOR
'・0xFFFFとの差を記録
'例:
'設定ボックス 記録される値
'1 FE FF (0xFFFE == 0xFFFF - (1 << 0))
'16 FF 7F (0x7FFF == 0xFFFF - (1 << 15))
'1 2 FC FF (0xFFFC == 0xFFFF - (1 << 0) - (1 << 1))
'【補足3】
'0x0007 227A~ // 頭
'0x0007 22CD~ // 上半身右
'・・・
'【補足4】
'float
'CD CC CC 3D == 0.1
'CD CC 4C 3E == 0.2
'CD CC CC 3E == 0.4
'CD CC 4C 3F == 0.8
'00 00 80 3F == 1.0
'CD CC CC 3F == 1.6
'【ジョイントについて】
'【補足1】
'constrain_pos_1[3]; // 制限:移動1(x, y, z)、constrain_pos_2[3]; // 制限:移動2(x, y, z)
'記録される順番に注意。
'設定ボックスの並びは、移動1x - 移動2x 移動1y - 移動2y 移動1z - 移動2z
'記録される値の並びは、移動1x 移動1y 移動1z 移動2x 移動2y 移動2z
'制限: 回転も同様。

 

PMDデータをExcel Sheetへ読出す


 前述の構造体の定義を行っていれば、PMDファイルからExcel Sheetへのモデル形状データの読み出しは容易だ。
ボタンをクリックしたときに読み出し処理を行う場合なら、例えば次のようにプログラムを記述することができる。

Private ss As String ' PMDデータ形式ファイル名
Private header As PMD_Header ' PMDデータファイルのヘッダー部
Private VertexList As PMD_VertexList ' PMDデータファイルの頂点データリスト
Private FaceList As PMD_FaceList ' PMDデータファイルの面データリスト
Private MaterialList As PMD_MaterialList ' PMDデータファイルの材質データリスト
Private i As Long


Private Sub CommandButton1_Click()

' ss = "C:\MyWeb\MMD\初音ミクVer2.pmd"
' ss = "C:\MyWeb\MMD\Haku_Yowane.pmd"
' ss = "C:\MyWeb\MMD\Meiko_Sakine.pmd"
   ss = "C:\MyWeb\MMD\Neru_Akita.pmd" '【読み出しPMDファイル名を指定する】
   Sheet4.Cells(1, 2) = ss
   Open ss For Binary Access Read As #1 ' 【PMDファイルをバイナリモードで開く】
'【PMDヘッダー】
      Get #1, , header ' 【PMDファイルからHeader型を読み込む】
      Sheet4.Cells(3, 3) = header.ID ' "Pmd"と書かれている
      Sheet4.Cells(4, 3) = header.Version ' バージョン番号が1.00のように単精度浮動小数点形式で記述されている。
      Sheet4.Cells(5, 3) = header.ModelName ' モデル名称
      Sheet4.Cells(6, 3) = header.Comment ' 著作権に関する情報が書かれている。
      Sheet4.Cells(6, 3).WrapText = False
'【PMD頂点リスト】
      Get #1, , VertexList.vert_count ' 【PMDファイルから頂点データの総数を得る】
      ReDim VertexList.vertex(VertexList.vert_count - 1) ' 【読込に必要な配列領域を確保する】
      Sheet4.Cells(9, 2) = VertexList.vert_count
      For i = 0 To VertexList.vert_count - 1 ' 【1頂点ずつデータを読み込む】一括して読み込むとエラーになるので要素ごとに分割読込
         Get #1, , VertexList.vertex(i).pos.x ' ■頂点のx,y,z座標
         Get #1, , VertexList.vertex(i).pos.y
         Get #1, , VertexList.vertex(i).pos.z
         Get #1, , VertexList.vertex(i).normal.x ' ■法線ベクトル
         Get #1, , VertexList.vertex(i).normal.y
         Get #1, , VertexList.vertex(i).normal.z
         Get #1, , VertexList.vertex(i).uv.u ' ■テクスチャーマッピングのu,v座標
         Get #1, , VertexList.vertex(i).uv.v
         Get #1, , VertexList.vertex(i).bone_num(0) ' ■どのBoneの支配下か
         Get #1, , VertexList.vertex(i).bone_num(1)
         Get #1, , VertexList.vertex(i).bone_weight
         Get #1, , VertexList.vertex(i).edge_flag ' ■エッジフラグ
         Sheet4.Cells(11 + i, 1) = i
         Sheet4.Cells(11 + i, 2) = VertexList.vertex(i).pos.x
         Sheet4.Cells(11 + i, 3) = VertexList.vertex(i).pos.y
         Sheet4.Cells(11 + i, 4) = VertexList.vertex(i).pos.z
         Sheet4.Cells(11 + i, 5) = VertexList.vertex(i).normal.x
         Sheet4.Cells(11 + i, 6) = VertexList.vertex(i).normal.y
         Sheet4.Cells(11 + i, 7) = VertexList.vertex(i).normal.z
         Sheet4.Cells(11 + i, 8) = VertexList.vertex(i).uv.u
         Sheet4.Cells(11 + i, 9) = VertexList.vertex(i).uv.v
         Sheet4.Cells(11 + i, 10) = VertexList.vertex(i).bone_num(0)
         Sheet4.Cells(11 + i, 11) = VertexList.vertex(i).bone_num(1)
         Sheet4.Cells(11 + i, 12) = VertexList.vertex(i).bone_weight
         Sheet4.Cells(11 + i, 13) = VertexList.vertex(i).edge_flag
      Next i
'【PMD面リスト】
      Get #1, , FaceList.face_vert_count ' 【面を構成する頂点ののべ総数】三角形なので面数×3になっている
      ReDim FaceList.face_vert_index(FaceList.face_vert_count - 1) ' 【読込に必要な配列領域を確保する】
      Sheet6.Cells(2, 2) = FaceList.face_vert_count
      j = 0
      For i = 0 To FaceList.face_vert_count - 1 Step 3 ' 【三角形単位に頂点インデックス番号を読込】
         Get #1, , FaceList.face_vert_index(i)
         Get #1, , FaceList.face_vert_index(i + 1)
         Get #1, , FaceList.face_vert_index(i + 2)
         Sheet6.Cells(4 + j, 1) = j
         Sheet6.Cells(4 + j, 2) = FaceList.face_vert_index(i)
         Sheet6.Cells(4 + j, 3) = FaceList.face_vert_index(i + 1)
         Sheet6.Cells(4 + j, 4) = FaceList.face_vert_index(i + 2)
         j = j + 1
      Next i
'【材質リスト】
      Get #1, , MaterialList.material_count ' 【登録材質総数を読み込む】
      ReDim MaterialList.material(MaterialList.material_count - 1) ' 【読込に必要な配列領域を確保する】
      Sheet7.Cells(2, 2) = MaterialList.material_count
      For i = 0 To MaterialList.material_count - 1 ' 【材質ごとに要素をまとめて読み込む】
         Get #1, , MaterialList.material(i)
         Sheet7.Cells(4 + i, 1) = i
         Sheet7.Cells(4 + i, 2) = MaterialList.material(i).material.faceColor.Red
         Sheet7.Cells(4 + i, 3) = MaterialList.material(i).material.faceColor.Green
         Sheet7.Cells(4 + i, 4) = MaterialList.material(i).material.faceColor.Blue
         Sheet7.Cells(4 + i, 5) = MaterialList.material(i).material.faceColor.alpha
         Sheet7.Cells(4 + i, 6) = MaterialList.material(i).material.power
         Sheet7.Cells(4 + i, 7) = MaterialList.material(i).material.specularColor.Red
         Sheet7.Cells(4 + i, 8) = MaterialList.material(i).material.specularColor.Green
         Sheet7.Cells(4 + i, 9) = MaterialList.material(i).material.specularColor.Blue
         Sheet7.Cells(4 + i, 10) = MaterialList.material(i).material.emissiveColor.Red
         Sheet7.Cells(4 + i, 11) = MaterialList.material(i).material.emissiveColor.Green
         Sheet7.Cells(4 + i, 12) = MaterialList.material(i).material.emissiveColor.Blue
         Sheet7.Cells(4 + i, 13) = MaterialList.material(i).toon_index
         Sheet7.Cells(4 + i, 14) = MaterialList.material(i).edge_flag
         Sheet7.Cells(4 + i, 15) = MaterialList.material(i).face_vert_count
         Sheet7.Cells(4 + i, 16) = MaterialList.material(i).texture_file_name
      Next i
Close #1
End Sub


図5 Sheet4(ヘッダー部と頂点データリスト)

図6 Sheet6(面リスト)

図7 Sheet7(材質リスト)

Excelでも行える3次元グラフィックス表示


【VBAでの画像表示法】
VBAで画像を表示する方法の選択肢はそれほど多くない。
オリジナルActive Xコントロールを作って張り込む方法はあるが、それだとインストール作業が必要になる。他のパソコンですぐに利用できない場合があるし、何よりコントロールのバージョンが一致しなければならず面倒だ。
そこで標準実装されている機能だけを使う方法を検討した。
Microsoft社のExcelPower PointVBA(Visual Basic for Application)に標準実装されているActive Xコントロールの中でグラフィックスを表示するために利用できるものはImageコントロール(Forms.Image)しかない。
ImageコントロールにはPictureプロパティがあり、画像ファイルを読み込んだり、書き込んだりすることができる。
開発タブにある挿入ボタンをクリックすると、図8のようにExcelシート内に張り込めるActive Xコントロールが表示される。中央下にあるを選んで、シート上の矩形領域を選択すれば、図9のように、灰色の矩形領域が表示され、同時にデザインモードボタンの色が変わるはずだ。このとき4隅をドラッグして大きさを調整することができる。
デザインモードボタンが選択されているときにimageコントロールをマウスでクリックして選択してから、ボタンを押すと、プロパティー・ウィンドウが表示される。Pictureと書かれた枠の右側にある枠内をダブル・クリックすることで図10に示すようにimageコントロール内に画像ファイルを読み込むことができる。 Imageコントロールが読み込むことのできるファイル形式はbmp, jpg, gif, ico, cur, wmfである。
VBA (Visual Basic for Application)でImageコントロールを用いた画像ファイルの読み込み/書き込みの操作を行うには以下のようにする。
プログラムで読み込む場合にはLoadPicture関数を用いる。反対にファイルとして書き出す場合にはSavePictureを使う。但し、読み出しは様々なファイル形式に対応しているが、書き出せるのはbmp形式に限定される。

■指定ファイルをImageコントロールに読み込む方法の例
   Image1.Picture = LoadPicture("ファイル名.bmp")
Imageコントロール内の画像データを指定ファイルに書き出す方法の例
   SavePicture Image1.Picture, "ファイル名.bmp"

  【実施例】
    Private Sub CommandButton4_Click()
      Image4.Picture = LoadPicture("C:\MyWeb\MMD\Ahoge2.jpg") ' jpg形式ファイルをImage4に読込、表示する。
      SavePicture Image4.Picture, "C:\MyWeb\MMD\Ahoge2.bmp" '
    End Sub

    Private Sub CommandButton5_Click()
      Image4.Picture = LoadPicture("C:\MyWeb\MMD\eyeM2.bmp") ' bmp形式ファイルをImage4に読込、表示する。
    End Sub

 取り扱えるファイル形式の中では、ソフト的にbmp形式の取扱が最も容易だろう。特に、1画素当たりRGB24bit(1670万色表示)の書式が最も目的に適合している。
 ImageコントロールにはPictureSizeModeというプロパティもあり、
 ●fmPictureSizeModeClipモード:元画像と同倍率で表示し、枠からはみ出た分は表示されない
 ●fmPictureSizeModeStretchモード:Imageコントロールの縦、横のサイズに自動的に調整される
 ●fmPictureSizeModeZoomモード:枠の短い方の長さに合わせて表示する
3つの表示モードがある。
 以上のように、1フレーム分のグラフィック描画処理をメモリ上で行い、完了後ファイル化してImageコントロールに読み込んで表示する方法をとることにした。これは実際のフレーム・メモリのダブル・バッファ(表示用と描画用を分けて持つ)方式に対応する。


【図8】Active Xコントロール

【図9】Imageコントロール
  
【図10】Imageコントロールのプロパティ

【bmpファイル形式について】
 bmp形式ファイルと言ってもいろいろなパターンがあるが、3次元グラフィックスを表示することが目的なら1画素がRGB24bitの書式が最もふさわしいだろう。
 RGB24bit形式のbmpファイルのヘッダー情報のフォーマットは次のようになっている。

Public Type RGB24bitBitMapHeader ' RGB24bitタイプのBitMapファイルのヘッダー
  B As Byte ' ファイル識別子 "B"
  M As Byte '       "M"
  FileLength As Long ' ファイルの長さ=ヘッダーサイズ(54バイト)+データサイズ
  Null1 As Long ' 0
  HeaderSize As Long ' ヘッダー領域のサイズ(54バイト)
  Offset As Long ' 画素データまでのオフセットサイズ(40バイト)
  Nx As Long ' x方向画素数
  Ny As Long ' y方向画素数
  NumberOfPlanes As Integer ' プレーンの数(1プレーン)
  BitsOfPixel As Integer ' 1画素を構成するビット数 (24ビット)
  Null2 As Long ' 0
  SizeOfData As Long ' 画素領域のバイト・サイズ
  Null3 As Long '
  Null4 As Long '
  Null5 As Long ' 0
  Null6 As Long ' 0
End Type


上から順に説明すると、
 ●最初の2バイト(B, M)はファイル識別子であり、アスキー・コードで"B"、"M"と書かれている。
 ●次の4バイトFileLengthはファイルの総バイト数である。下の例では0x000CC44A=836682バイトである。
 ●次の4バイトはnull(意味がない隙間)である。
 ●次の4バイトHeaderSizeはヘッダー情報のバイト数を示している。下の例は0x36=54バイトである。
 ●次の4バイトOffsetは画像データ領域までのオフセット値を示す。この情報を含めて40バイトである。
 ●次の4バイトNxx方向の画素数を示す。例では0x00000219=537画素である。
 ●次の4バイトNyy方向の画素数を示す。例では0x00000207=519画素である。
 ●次の2バイトNumberOfPlanesはフレーム・メモリの何画面分(プレーン数)を持っているかを示す。例は1である。
 ●次の2バイトBitsOfPixelは1画素を構成するビット数を示す。例では24である。
以上がヘッダー情報部分の概略である。

 
【図11】ヘッダー情報部分


RGB24bit形式のbmpファイルの場合、画像データ部分は次のようになっている。
簡単のため、横7画素、縦3画素とすれば、
 BGRBGRBGRBGRBGRBGRBGR*
 BGRBGRBGRBGRBGRBGRBGR*
 BGRBGRBGRBGRBGRBGRBGR*
と並んでいる。ここでスキャンラインの最後尾に付いている*はスキャンライン1本分のデータ長を必ず4の倍数にしなければならないのでワード境界を揃えるために必要なバイトだ。これは画像の横幅画素数が4の倍数になっている場合には必要ない。
この『ワード境界問題』と『生データの並び順(B,G,R)』さえ忘れなければ、VBA(Visual Basic for Application)は2次元配列で簡単にアクセスすることが可能だ。

Public Type NTL_ColorRGB ' 画素データの構造(RGB24bitタイプ)
  B As Byte ' 青(0~255)
  G As Byte ' 緑(0~255)
  R As Byte ' 赤(0~255)
End Type

と宣言しておいて、

Dim PixcelBuffer() As NTL_ColorRGB

Nx=7: Ny=3
Redim PixcelBuffer(Nx-1, Ny-1)

とすれば2次元配列で画素単位のアクセスが可能になる。


     


MikuMikuDanceのモデルデータ
  1) 初音ミク用Excel VBA
  2) 亞北ネル用Excel VBA
  3) 鏡音リン用Excel VBA
  4) 弱音ハク用Excel VBA
  5) 咲音メイコ用Excel VBA
■記載日2009年9月27日■

3次元グラフィックス・ライブラリ


[Lib_3DGraphic.bas]
まだ、いろいろとできていないが、PMDデータをExcel VBAでSheetに読み込んで全てソフトウェアで3次元グラフィックス表示を行うビュワーの開発を開始。今現在の結果を示す。
DirectXやWin32APIも一切使わず、VBAだけでRGB24bitのbmpファイルを作成し、Imageオブジェクトに表示する方式をとっている。
ポリゴン・フィルのバグ取り後をした後でテクスチャーマッピングまで実装しています。この後の予定としては透視変換、表情(skin)を実装し、Boneにトライする予定。
【備考欄】2009/10/02 透視変換まで実装完了しました。


■記載日2009年9月30日■
【Excel VBAでMMDモデルを表示】
Lib_3DGraphic.basをコーディング・デバッグに約14時間程かけて作った。なので、まだ透視変換も光源計算(ライティング)も実装していないが、3次元陰面処理をかけてポリゴン・フィルやテクスチャーマッピングは実装済みだ。ちゃんとRGB24bit(1670万色)表示に対応しているので光源計算が付与されればそれなりに見れる画像が作れるようになると思う。
このライブラリを使えば、Excel VBAで表示するのは簡単だ。ソースの実例を示す。


Private MyPic As NTL2D_BitMapRGB ' RGB24bit形式のBITMAP構造体(Colorテーブルも含む)
Private My3DCG As NTL3D_BitMapRGB ' RGB24bit形式のBITMAP構造体(Colorテーブルも含む)
Private s As String ' BITMAPファイル名称を格納するための文字列型変数
Private ss As String ' PMDデータ形式ファイル名
Private Header As PMD_Header ' PMDデータファイルのヘッダー部
Private VertexList As PMD_VertexList ' PMDデータファイルの頂点データリスト
Private FaceList As PMD_FaceList ' PMDデータファイルの面データリスト
Private MaterialList As PMD_MaterialList ' PMDデータファイルの材質データリスト
Private i As Long

Private Sub CommandButton2_Click()
  Dim i As Double
  Const Dx As Long = 800 ' x方向イメージ画素数
  Const Dy As Long = 800 ' y方向イメージ画素数
  Const Ox_xy As Long = 265 ' xy方向描画時のx方向イメージ・オフセット265*5=1325
  Const Ox_zy As Long = 600 ' zy方向描画時のx方向イメージ・オフセット600*5=3000
  Const Oy As Long = 2 ' 描画時のy方向イメージ・オフセット
  Const K As Single = 32 ' 描画倍率 32*5=160
  Dim MColor As NTL_ColorRGB ' 描画時背景色
  Dim P1 As NTL_ColorRGB, P2 As NTL_ColorRGB, P3 As NTL_ColorRGB
  Dim fP1 As x_ColorRGBA, fP2 As x_ColorRGBA
  '【2次元デモ/3次元テクスチャーマッピング用】
  Dim uv1 As x_Coords2d, uv2 As x_Coords2d, uv3 As x_Coords2d
  '【3次元デモ用】
  Dim pos1 As x_Vector, pos2 As x_Vector, pos3 As x_Vector



'【頂点データ読込】
  VertexList.vert_count = Sheet4.Cells(9, 2)
  ReDim VertexList.vertex(VertexList.vert_count - 1)
  For i = 0 To VertexList.vert_count - 1
    VertexList.vertex(i).pos.x = Sheet4.Cells(11 + i, 2)
    VertexList.vertex(i).pos.y = Sheet4.Cells(11 + i, 3)
    VertexList.vertex(i).pos.z = Sheet4.Cells(11 + i, 4)
    VertexList.vertex(i).normal.x = Sheet4.Cells(11 + i, 5)
    VertexList.vertex(i).normal.y = Sheet4.Cells(11 + i, 6)
    VertexList.vertex(i).normal.z = Sheet4.Cells(11 + i, 7)
    VertexList.vertex(i).uv.u = Sheet4.Cells(11 + i, 8)
    VertexList.vertex(i).uv.v = Sheet4.Cells(11 + i, 9)
    VertexList.vertex(i).bone_num(0) = Sheet4.Cells(11 + i, 10)
    VertexList.vertex(i).bone_num(1) = Sheet4.Cells(11 + i, 11)
    VertexList.vertex(i).bone_weight = Sheet4.Cells(11 + i, 12)
    VertexList.vertex(i).edge_flag = Sheet4.Cells(11 + i, 13)
  Next i
'【面データ読込】
  FaceList.face_vert_count = Sheet6.Cells(2, 2)
  ReDim FaceList.face_vert_index(FaceList.face_vert_count - 1)
  j = 0
  For i = 0 To FaceList.face_vert_count - 1 Step 3
    FaceList.face_vert_index(i) = Sheet6.Cells(4 + j, 2)
    FaceList.face_vert_index(i + 1) = Sheet6.Cells(4 + j, 3)
    FaceList.face_vert_index(i + 2) = Sheet6.Cells(4 + j, 4)
    j = j + 1
  Next i
'【材質データ読込】
  MaterialList.material_count = Sheet7.Cells(2, 2)
  ReDim MaterialList.material(MaterialList.material_count - 1)
  For i = 0 To MaterialList.material_count - 1
    MaterialList.material(i).material.FaceColor.Red = Sheet7.Cells(4 + i, 2)
    MaterialList.material(i).material.FaceColor.Green = Sheet7.Cells(4 + i, 3)
    MaterialList.material(i).material.FaceColor.Blue = Sheet7.Cells(4 + i, 4)
    MaterialList.material(i).material.FaceColor.Alpha = Sheet7.Cells(4 + i, 5)
    MaterialList.material(i).material.Power = Sheet7.Cells(4 + i, 6)
    MaterialList.material(i).material.SpecularColor.Red = Sheet7.Cells(4 + i, 7)
    MaterialList.material(i).material.SpecularColor.Green = Sheet7.Cells(4 + i, 8)
    MaterialList.material(i).material.SpecularColor.Blue = Sheet7.Cells(4 + i, 9)
    MaterialList.material(i).material.EmissiveColor.Red = Sheet7.Cells(4 + i, 10)
    MaterialList.material(i).material.EmissiveColor.Green = Sheet7.Cells(4 + i, 11)
    MaterialList.material(i).material.EmissiveColor.Blue = Sheet7.Cells(4 + i, 12)
    MaterialList.material(i).toon_index = Sheet7.Cells(4 + i, 13)
    MaterialList.material(i).edge_flag = Sheet7.Cells(4 + i, 14)
    MaterialList.material(i).face_vert_count = Sheet7.Cells(4 + i, 15)
    MaterialList.material(i).texture_file_name = Sheet7.Cells(4 + i, 16)
  Next i
' *****【データの読み込み処理】ここまで ***********************************


' ***********************************************************************
' *****【3次元グラフィックス・モデル表示処理】ここから
' ***********************************************************************
  Eye$ = "C:\MyWeb\MMD\eye3Ne.bmp" ' 亞北ネルの眼のテクスチャー
  TEX% = 13 ' テクスチャーを張り付ける材質番号
  BmpFileName$ = "\RinKagamine_800x800.bmp" ' 3次元グラフィックス出力ファイル

  '【テクスチャー・マッピング・ファイルを読み込む】眼のデータ
  NTL2D_GetBitMapFile MyPic, Eye$
  '【3次元フレームメモリを確保】
  NTL3D_BuildBitMap Dx, Dy, My3DCG ' 横Dx画素、縦Dy画素のbmp形式オブジェクト・データMy3DCGを作成。
  MColor.R = 40: MColor.G = 200: MColor.B = 90 ' 背景色R=40 G=200 B=90
  NTL3D_FillBitMapImage My3DCG, MColor ' 画面全体を背景色で塗りつぶす。
  '【フレーム枠を描画する】
  pos1.x = 0: pos1.y = 0: pos1.z = -100: pos2.x = Dx - 1: pos2.y = 0: pos2.z = -100
  NTL3D_DrawLineConstant My3DCG, pos1, pos2, My3DCG.BasicColor.Red ' MyPicの枠を黒色で描画する。
  pos1.x = Dx - 1: pos1.y = 0: pos1.z = -100: pos2.x = Dx - 1: pos2.y = Dy - 1: pos2.z = -100
  NTL3D_DrawLineConstant My3DCG, pos1, pos2, My3DCG.BasicColor.Red ' MyPicの枠を黒色で描画する。
  pos1.x = Dx - 1: pos1.y = Dy - 1: pos1.z = -100: pos2.x = 0: pos2.y = Dy - 1: pos2.z = -100
  NTL3D_DrawLineConstant My3DCG, pos1, pos2, My3DCG.BasicColor.Red ' MyPicの枠を黒色で描画する。
  pos1.x = 0: pos1.y = Dy - 1: pos1.z = -100: pos2.x = 0: pos2.y = 0: pos2.z = -100
  NTL3D_DrawLineConstant My3DCG, pos1, pos2, My3DCG.BasicColor.Red ' MyPicの枠を黒色で描画する。

'【xy平面から見た初音ミク】
  ' xy平面から見た初音ミクを横オフセットOx_xy、縦オフセットOy、倍率はK倍で描画する。
  CP% = 0
  CC% = 0
  For i = 0 To FaceList.face_vert_count - 1 Step 3 ' MMDではポリゴンは三角形単位
    pos1.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.x) + Ox_xy
    pos1.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.y) + Oy
    pos1.z = CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.z)
    pos2.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.x) + Ox_xy
    pos2.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.y) + Oy
    pos2.z = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.z)
    pos3.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.x) + Ox_xy
    pos3.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.y) + Oy
    pos3.z = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.z)
    If CC% <= 0 Then
      CC% = Sheet7.Cells(4 + CP%, 15)
      P1.R = 255 * Sheet7.Cells(4 + CP%, 2)
      P1.G = 255 * Sheet7.Cells(4 + CP%, 3)
      P1.B = 255 * Sheet7.Cells(4 + CP%, 4)
      CP% = CP% + 1
    End If
    CC% = CC% - 3
    If CP% = TEX% Then
      uv1.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i)).uv.u
      uv1.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i)).uv.v
      uv2.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i + 1)).uv.u
      uv2.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i + 1)).uv.v
      uv3.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i + 2)).uv.u
      uv3.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i + 2)).uv.v
      NTL3D_TriangleWithTextureMapping My3DCG, MyPic, pos1, uv1, pos2, uv2, pos3, uv3
  Else
      NTL3D_Triangle My3DCG, pos1, P1, pos2, P1, pos3, P1
    End If
    DoEvents
  Next i

  ' zy平面から見た初音ミクを横オフセットOx_zy、縦オフセットOy、倍率はK倍で描画する。
  CP% = 0
  CC% = 0
  For i = 0 To FaceList.face_vert_count - 1 Step 3 ' MMDではポリゴンは三角形単位
    pos1.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.z) + Ox_zy
    pos1.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.y) + Oy
    pos1.z = -CLng(K * VertexList.vertex(FaceList.face_vert_index(i)).pos.x)
    pos2.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.z) + Ox_zy
    pos2.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.y) + Oy
    pos2.z = -CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 1)).pos.x)
    pos3.x = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.z) + Ox_zy
    pos3.y = CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.y) + Oy
    pos3.z = -CLng(K * VertexList.vertex(FaceList.face_vert_index(i + 2)).pos.x)
    If CC% <= 0 Then
    CC% = Sheet7.Cells(4 + CP%, 15)
    P1.R = 255 * Sheet7.Cells(4 + CP%, 2)
    P1.G = 255 * Sheet7.Cells(4 + CP%, 3)
    P1.B = 255 * Sheet7.Cells(4 + CP%, 4)
    CP% = CP% + 1
  End If
  CC% = CC% - 3
  If CP% = TEX% Then
    uv1.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i)).uv.u
    uv1.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i)).uv.v
    uv2.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i + 1)).uv.u
    uv2.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i + 1)).uv.v
    uv3.u = (MyTex.Header.Nx - 1) * VertexList.vertex(FaceList.face_vert_index(i + 2)).uv.u
    uv3.v = (MyTex.Header.Ny - 1) * VertexList.vertex(FaceList.face_vert_index(i + 2)).uv.v
    NTL3D_TriangleWithTextureMapping My3DCG, MyPic, pos1, uv1, pos2, uv2, pos3, uv3
  Else
    NTL3D_Triangle My3DCG, pos1, P1, pos2, P1, pos3, P1
  End If
  DoEvents
  Next i

  ' 描画を終了後、ファイル化し、ExcelのSheet内のimageオブジェクトに読込、表示する。
  s = CurDir + BmpFileName$
  NTL3D_CreateBitMapFile My3DCG, s ' 描画を終了したbmp形式オブジェクト・データMyPicをファイル名sとして作成する。
  Image1.Picture = LoadPicture(s) ' bmp形式ファイルをImage1に読込、表示する。
End Sub


【Lib_3DGraphic.bas】 ' *******************************************************************************
' *** 3D Computer Graphics Library (Microsoft Excel VBA)
' ***                Version 0.47 (23th September, 2009)
' ***                Copyright ☆Tomoaki Ueda☆ (Japan)
' ***                     [Neo-Tech-Lab Co., Ltd.]
' ***【使用許諾条件】
' *** 1) 本プログラムは『私的利用』に限りお使いいただけるフリーウェアです。
' *** 従いまして、商用利用、法人でのご利用、販売はお断りいたします。
' *** 2) このプログラムを用いて製作された画像は勿論自由にお使いいただけます。
' *** 3) 作者は多忙のため本プログラムの技術サポートを行うことはありません。
' *** バグ対応、個別の問合せに対する返答はいたしません。
' *** 4) 本プログラムの利用による一切の責任はご利用者自身にあります。
' *** 例え如何なる問題が生じても作者が責任を負うことはありません。
' *******************************************************************************

'【仕様】
' 本プログラムは、製作期間2~3日間程度で製作できる程度の簡易的3次元グラフィックス処理に限定して作成したプログラムです。 Microsoft社のExcel VBAで簡易3次元グラフィックス表示を行うにあたり、このライブラリでは以下の前提条件を適用しています。
' 1) ポリゴンは三角形に限定。
' 2) 物体色のみ利用。光源計算を行わない。
' 3) 透視変換を行わない。
' 4) 三角形の法線ベクトル方向による裏ポリゴンの非表示処理を行わない。
' 5) クリッピング処理を行わない。
' 6) カーニングを行わない。
' つまり、物体色あるいはテクスチャーマッピングによる表示色で、zバッファ法での3次元陰面処理による三角形表示しか行いません。

' *************************************
' *****【DirectX の 基本データ型】*****
' *************************************
' DirectXで使用される基本データ型を定義します。本ビューワーで使うのは以下の5つのタイプです。
' 1) x_Vectorは3次元の頂点座標やベクトルを定義するために使います。
' 2) x_Coords2dは主としてテクスチャーマッピングで使うuv座標を定義するために使います。
' 3) x_ColorRGBAは物体色を定義するために使います。R,G,B,A共に範囲は0.0~1.0です。A(alpha)は透過率を示します。
' 4) x_ColorRGBは鏡面反射係数や散乱光係数を定義するために用います。R,G,B共に範囲は0.0~1.0です。
' 5) x_materialは物体の光源計算に必要な物体色、鏡面反射係数、反射強度、散乱光係数を定義する材質を示します。

Public Type x_Vector '【頂点座標、ベクトル】
  x As Single ' x座標(単精度浮動小数点形式4Bytes)
  y As Single ' y座標(単精度浮動小数点形式4Bytes)
  z As Single ' z座標(単精度浮動小数点形式4Bytes)
End Type

Public Type x_Coords2d '【テクスチャーマッピング用UV座標】
  u As Single ' u座標(単精度浮動小数点形式4Bytes)
  v As Single ' v座標(単精度浮動小数点形式4Bytes)
End Type

Public Type x_ColorRGBA '【物体色】
  Red As Single ' 赤(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Green As Single ' 緑(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Blue As Single ' 青(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Alpha As Single ' 透過率(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
End Type

Public Type x_ColorRGB '【鏡面反射色、散乱光色】
  Red As Single ' 赤(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Green As Single ' 緑(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
  Blue As Single ' 青(単精度浮動小数点形式4Bytes, 範囲0.00-1.00)
End Type

Public Type x_Material '【材質定義】
  FaceColor As x_ColorRGBA ' 物体色
  Power As Single ' 反射強度(単精度浮動小数点形式4Bytes)
  SpecularColor As x_ColorRGB ' 鏡面反射色
  EmissiveColor As x_ColorRGB ' 散乱光色
End Type


'*************************************************************************
'********************【MikuMikuDanColore独自の構造体】********************
'*************************************************************************
' MikuMikuDanceで使われている独自の構造体をまとめました。
' このプログラムでは最初の2タイプを使用しています。
' なお、MikuMikuDance独自の構造体及び、PMDファイルの構造についての詳細はこちらのページ(通りすがりの記憶)のお世話になりました。
Public Type PMD_vertex '【PMD_頂点データ】 4*3+4*3+4*2+2*2+2 = 12+12+8+4+2 = 38bytes/頂点
  pos As x_Vector '・【頂点座標】x,y,z
  normal As x_Vector '・【法線ベクトル】nx,ny,nz
  uv As x_Coords2d '・【uv座標】MMDは頂点uv(範囲0.0-1.0) ◆読込時に範囲u:0-(cg.Header.Nx-1),範囲v:0-(cg.Header.Ny-1)に変換する。
  bone_num(1) As Integer '・【Bone番号】Bone番号1, Bone番号2 モデル変形(頂点移動)時に影響
  bone_weight As Byte '・【Bone重み係数】Bone1に与える影響度 0-100 Bone2への影響度は(100-bone_weight)で与えられる。
  edge_flag As Byte '・【エッジ・フラグ】0:通常、1:エッジ無効 輪郭線が有効の場合
End Type '

Public Type PMD_material '【PMD_材質データ】
  material As x_Material '・【材質データ】DirectXの材質データ構造体と同じ
  toon_index As Byte '・【トゥーン・インデックス番号】toon??.bmp 0.bmp:0xFF, 1.bmp:0x00, ・・・10.bmp:0x09
  edge_flag As Byte '・【エッジ・フラグ】輪郭、影
  face_vert_count As Long '・【面頂点数】面数*3 = 面頂点数 実際のindexに直すには材質0から累積加算する。
  texture_file_name As String * 20 '・【テクスチャ・ファイル名】20bytesギリギリまで使える 20bytes時は0x00がなくても可。'
End Type

Public Type PMD_bone '【PMD_Boneデータ】
  bone_name As String * 20 '・【Bone名】Boneの名称
  parent_bone_index As Integer '・【親Bone番号】(無い場合は0xFFFF)
  tail_pos_bone_index As Integer '・【tail位置のBone番号】(chain末端は0xFFFF) 親:子は1:多なので位置決め用
  bone_type As Byte '・【Boneの種類】0:回転のみ 1:回転と移動 2:IK 3:不明 4:IK影響下 5:回転影響下 6:IK接続先 7:非表示
  bone_head_pos As x_Vector '・【Boneのヘッド座標】x,y,z             ↑注) Boneの種類(MMD 4.0~) 8:捻り 9:回転運動
End Type

Public Type PMD_IK_data '【PMD_IK_data】
  IK_bone_index As Integer ' IK Bone番号
  IK_target_bone_index As Integer ' IKターゲットBone番号 IK Boneが最初に接続するBone
  IK_chain_length As Byte ' IKチェーンの長さ(子の数)
  Iterations As Integer ' 再帰演算回数 IK値1
  Control_weight As Single ' IKの影響度 IK値2
  Ik_child_bone_index() As Integer ' 配列サイズはIK_chain_length IK影響下のBone番号
End Type

Public Type PMD_Skin_vert_data '【PMD_Skin_vert_data】 (type:base) | (type:base以外)
  Skin_vert_index As Long ' 表情用の頂点の番号(頂点リストにある番号) | 表情用の頂点の番号(baseの番号。skin_vert_index)
  Skin_vert_pos As x_Vector ' x,y,z 表情用頂点座標(頂点自体の座標) | x,y,z 表情用頂点の座標オフセット値(baseに対するオフセット)
End Type

Public Type PMD_Skin_data '【PMD_Skin_data】
  Skin_name As String * 20 ' char skin_name[20] 表情名
  Skin_vert_count As Long ' 表情用頂点数
  Skin_type As Byte ' 表情の種類 0:base, 1:まゆ, 2:眼, 3:リップ, 4:その他
  Skin_vert_data() As PMD_Skin_vert_data '配列サイズはSkin_vert_count 表情用の頂点データ(16bytes/頂点)
End Type

Public Type PMD_Bone_disp '【PMD_Bone_disp】
  Bone_index As Integer 'WORD bone_index; // 枠用Bone番号
  Bone_disp_frame_index As Byte 'BYTE bone_disp_frame_index; // 表示枠番号
End Type

Public Type PMD_RigidBody '【PMD_RigidBody】
  RigidBody_name As String * 20 ' 剛体名 // 頭
  RigidBody_rel_bone_index As Integer ' 剛体関連ボーン番号 // 03 00 == 3 // 頭
  RigidBody_group_index As Byte ' 剛体グループ番号 // 00
  RigidBody_group_target As Integer ' 剛体グループ:対象 // 0xFFFFとの差 // 38 FE
  Shape_type As Byte ' 剛体形状:タイプ(0:球、1:箱、2:カプセル) // 00 // 球
  Shape_w As Single ' 剛体形状:半径(幅) // CD CC CC 3F // 1.6
  Shape_h As Single ' 剛体形状:高さ // CD CC CC 3D // 0.1
  Shape_d As Single ' 剛体形状:奥行 // CD CC CC 3D // 0.1
  Pos_pos As x_Vector ' 位置:位置(x, y, z)
  Pos_rot As x_Vector ' 位置:回転(rad(x), rad(y), rad(z))
  RigidBody_weight As Single ' 剛体:質量 // 00 00 80 3F // 1.0
  RigidBody_pos_dim As Single ' 剛体:移動減 // 00 00 00 00
  RigidBody_rot_dim As Single ' 剛体:回転減 // 00 00 00 00
  RigidBody_recoil As Single ' 剛体:反発力 // 00 00 00 00
  RigidBody_friction As Single ' 剛体:摩擦力 // 00 00 00 00
  RigidBody_type As Byte ' 剛体:タイプ(0:Bone追従、1:物理演算、2:物理演算(Bone位置合せ)) // 00 // Bone追従
End Type

Public Type PMD_Joint '【t_joint】
  Joint_name As String * 20 ' ジョイント名称 // 右髪1
  Joint_rigidbody_a As Long ' ジョイント:剛体A
  Joint_rigidbody_b As Long ' ジョイント:剛体B
  Joint_pos As x_Vector ' ジョイント:位置(x, y, z) // 諸データ:位置合せでも設定可
  Joint_rot As x_Vector ' ジョイント:回転(rad(x), rad(y), rad(z))
  Constrain_pos_1 As x_Vector ' 制限:移動1(x, y, z)
  Constrain_pos_2 As x_Vector ' 制限:移動2(x, y, z)
  Constrain_rot_1 As x_Vector ' 制限:回転1(rad(x), rad(y), rad(z))
  Constrain_rot_2 As x_Vector ' 制限:回転2(rad(x), rad(y), rad(z))
  Spring_pos As x_Vector ' ばね:移動(x, y, z)
  Spring_rot As x_Vector ' ばね:回転(rad(x), rad(y), rad(z))
End Type


'*********************************************************************
'**********************【PMDファイルの構成要素】**********************
'*********************************************************************
' 実際のPMDファイルの構造を示します。本プログラムでは太字部分の4つのブロックしか使っていません。
Public Type PMD_Header '【ヘッダー】
  ID As String * 3 ' char magic[3]; // "Pmd"
  Version As Single ' float version[4]; // 0x00 0x00 0x80 0x3F == 1.00
  ModelName As String * 20 ' char model_name[20]; // モデル名称
  Comment As String * 256 ' char comment[256]; // コメント欄
End Type ' 注) 文字列:終端0x00 パディング0xFD 文字コード:shift JIS

Public Type PMD_VertexList '【頂点リスト】一般にVertexリストと呼ばれています。
  vert_count As Long ' DWORD vert_count; // 頂点数
  vertex() As PMD_vertex ' PMD_vertex vertex[vert_count]; // 頂点データ(38bytes/頂点)
End Type

Public Type PMD_FaceList '【面リスト】MikuMikuDanceのモデルではtriangle(三角形)しか使っていません。
  face_vert_count As Long ' DWORD face_vert_count; // 面数(三角形の数)*3 この配列の要素数
  face_vert_index() As Integer ' WORD face_vert_index[face_vert_count]; // 頂点番号(3個/面)
End Type ' 注)この配列のためにMMDでは最大65535頂点までのモデルしか取り扱えない。

Public Type PMD_MaterialList '【材質リスト】material
  material_count As Long ' DWORD material_count; // 材質数
  material() As PMD_material ' t_material material[material_count]; // 材質データ(70Bytes/material)
End Type

Public Type PMD_BoneList '【Boneリスト】
  bone_count As Integer ' WORD bone_count; // Bone数
  Bone() As PMD_bone ' t_bone bone[bone_count]; // Boneデータ
End Type

Public Type PMD_IK_List '【IKリスト】
  IK_data_count As Integer ' WORD ik_data_count; // IKデータ数
  IK_data() As PMD_IK_data ' PMD_ik_data ik_data[ik_data_count]; // IKデータ (11 + 2*ik_chain_length)/IK
End Type

Public Type PMD_SkinList '【表情リスト】
  Skin_count As Integer ' WORD skin_count; // 表情数
  Skin_data() As PMD_Skin_data 't_skin_data skin_data[skin_count]; // 表情データ
End Type

Public Type PMD_SkinDispList '【表情枠用表示リスト】
  Skin_dsip_count As Byte 'BYTE skin_disp_count; // 表情枠に表示する表情数
  Skin_index() As Integer 'WORD skin_index[skin_disp_count]; // 表情番号
End Type

Public Type PMD_BoneDispNameList '【Bone枠用枠名リスト】
  Bone_disp_name_count As Byte 'BYTE bone_disp_name_count; // Bone枠用の枠名数
  Disp_name As String * 50 'char disp_name[50][bone_disp_name_count]; // 枠名(50Bytes/枠)
End Type

Public Type PMD_BoneDispList '【Bone枠用表示リスト】
  Bone_disp_count As Long 'DWORD bone_disp_count; // Bone枠に表示するBone数(枠0センターを除く全てのBoneの合計)
  Bone_disp() As PMD_Bone_disp ' bone_disp[bone_disp_count]; // 枠用Boneデータ(3Bytes/枠)
End Type
'注)bone_disp_countの部分は
'BYTE bone_disp_count + 0x0000(Bone0) + 0x00(枠0)
'のように見えますが、DWORDです。
'*BYTEにすると総数が合わない(0x000000をデータとしてカウントすると1足らない)
'*Bone番号がWORDなので、WORD以上のサイズが必要
'*データの区切りの位置から判断すると、WORDにはならない。

'**********************【PMD拡張部分】*********************
Public Type PMD_Header_eg '【英語メニュー表示拡張】
  English_name_compatibility As Byte 'フラグ 01:英名対応あり
  Model_name As String * 20 '【英語ヘッダー】モデル名model_name_eg[20]
  Comment As String * 256 '     comment_eg[256]; // コメント(英語)
  Bone_name_eg() As String * 20 '【Boneリスト】char bone_name_eg[20][bone_count]; // Bone名(英名)
  Skin_name_eg() As String * 20 '【表情リスト】char skin_name_eg[20][skin_count-1]; // 表情名(英名) baseは英名が登録されないため
  Disp_name_eg() As String * 50 '【Bone枠用枠名リスト】char disp_name_eg[50][bone_disp_name_count]; // 枠名(英名) MMDでは区分名 Centerは英名は登録されない。
End Type

'********************【トゥーンテクスチャ】********************
Public Type PMD_ToonTextureList '【トゥーンテクスチャリスト】(Toon指定)
  toon_filename(9) As String * 100 'トゥーンテクスチャファイル名 toon_file_name[100][10]
End Type

'********************【Bullet物理演算】********************
Public Type PMD_RigidBodyList '【物理演算_剛体リスト】
  RigidBody_count As Long 'DWORD rigidbody_count; // 剛体数 // 2D 00 00 00 == 45
  RigidBody() As PMD_RigidBody 't_rigidbody[rigidbody_count]; // 剛体データ(83Bytes/rigidbody)
End Type

Public Type PMD_JointList '【物理演算_ジョイントリスト】 0x0007 3111~0x0007 3E28(ファイル末尾)
  Joint_count As Long 'DWORD joint_count; // ジョイント数 // 1B 00 00 00 == 27
  Joint() As PMD_Joint 't_joint[joint_count]; // ジョイントデータ(124Bytes/joint)
End Type

'【剛体について】
'【補足1】
'pos_rot[3]; // 位置:回転
'記録される値は、設定ボックスの値(度)をラジアンに変換した値(若干の誤差あり)
'※1 記録される値 = 設定ボックスの値 * Pi / 180
'※2 PI == 3.1415920
'※3 有効桁数は8桁
'※4 設定ボックスに表示されていない桁も計算対象とするようです。(値をペーストした場合など)
'※5 記録される値は2*PI(360度)で制限されません
'【補足2】
'rigidbody_group_target; // 諸データ:グループ:対象
'・各値を(設定値-1)左シフトした後、ビットOR
'・0xFFFFとの差を記録
'例:
'設定ボックス 記録される値
'1 FE FF (0xFFFE == 0xFFFF - (1 << 0))
'16 FF 7F (0x7FFF == 0xFFFF - (1 << 15))
'1 2 FC FF (0xFFFC == 0xFFFF - (1 << 0) - (1 << 1))
'【補足3】
'0x0007 227A~ // 頭
'0x0007 22CD~ // 上半身右
'・・・
'【補足4】
'float
'CD CC CC 3D == 0.1
'CD CC 4C 3E == 0.2
'CD CC CC 3E == 0.4
'CD CC 4C 3F == 0.8
'00 00 80 3F == 1.0
'CD CC CC 3F == 1.6
'【ジョイントについて】
'【補足1】
'constrain_pos_1[3]; // 制限:移動1(x, y, z)、constrain_pos_2[3]; // 制限:移動2(x, y, z)
'記録される順番に注意。
'設定ボックスの並びは、移動1x - 移動2x 移動1y - 移動2y 移動1z - 移動2z
'記録される値の並びは、移動1x 移動1y 移動1z 移動2x 移動2y 移動2z
'制限: 回転も同様。

' ***********************************************
' *****【NTL3Dの3Dオブジェクト・データ型】*****
' ***********************************************
' 本プログラムで2次元及び3次元の描画処理に使用する構造体を定義しています。
' 1) 1画素RGB24ビットのbmp形式ファイルの構造を利用して描画処理を行っています。
' 2) 1画素3バイト単位のため、画像の横幅によって1スキャンライン毎にワード境界の問題が発生します。
' 3) 描画処理の時に基本色が定義されていると色の設定が楽になることが多いのでBasicColorを定義しています。
' 4) NTL_BitMapRGB_HeaderはWindowsで使用されているRGB24bitタイプのBitMapファイルのヘッダー部分の構造を示しています。
' 5) 2次元描画のためにNTL2D_BitMapRGBを、3次元描画のためにNTL3D_BitMapRGBを用意しました。
'  違いはzバッファの有無だけです。テクスチャーマッピングデータは2次元を使います。

Public Const NTL3D_Z_max As Single = 3.402823E+38 '【NTL単精度】zバッファで使用する最奥値です。

Public Type NTL_ColorRGB ' 画素データの構造(RGB24bitタイプ)
  B As Byte ' 青(0~255)
  G As Byte ' 緑(0~255)
  R As Byte ' 赤(0~255)
End Type

Public Type NTL_BasicColor ' オブジェクトを描画する際に用いる固定色
  White As NTL_ColorRGB ' 白 255 255 255
  Black As NTL_ColorRGB ' 黒 0 0 0
  Red As NTL_ColorRGB ' 赤 255 0 0
  Orange As NTL_ColorRGB ' オレンジ 255 128 0
  Yellow As NTL_ColorRGB ' 黄 255 255 0
  Green As NTL_ColorRGB ' 緑 0 255 0
  Cyan As NTL_ColorRGB ' シアン 0 255 255
  Blue As NTL_ColorRGB ' 青 0 0 255
  Violet As NTL_ColorRGB ' 紫 255 0 128
  Magenta As NTL_ColorRGB ' マゼンタ 255 0 255
  Brown As NTL_ColorRGB ' 茶 128 0 0
  DarkGray As NTL_ColorRGB ' 濃灰  50 50 50
  Gray As NTL_ColorRGB ' 灰  128 128 128
  LightGray As NTL_ColorRGB ' 淡灰 200 200 200
  DarkGreen As NTL_ColorRGB ' 濃緑  0 128 0
End Type

Public Type NTL_BitMapRGB_Header ' RGB24bitタイプのBitMapファイルのヘッダー
  ID As String * 2 ' ファイル識別子 "BM"
  FileLength As Long ' ファイルの長さ = ヘッダーサイズ (54バイト) + データサイズ( (x方向画素数×3に最も近い4の倍数)×(y方向画素数) )
  Null1 As Long ' **ヌル領域** 0
  HeaderSize As Long ' ヘッダー領域のサイズ (54バイト)
  Offset As Long ' 画素データまでのオフセットサイズ(40バイト)
  Nx As Long ' x方向画素数
  Ny As Long ' y方向画素数
  NumberOfPlanes As Integer ' プレーンの数 (1プレーン)
  BitsOfPixel As Integer ' 1画素を構成するビット数 (24ビット)
  Null2 As Long ' **ヌル領域** 0
  SizeOfData As Long ' 画素領域のバイト・サイズ (x方向画素数×3に最も近い4の倍数)×(y方向画素数)
  Null3(3) As Long ' **ヌル領域** 0,0,0,0
End Type

Public Type NTL2D_BitMapRGB ' ***** 2D描画/テクスチャーマッピング *****
  Header As NTL_BitMapRGB_Header '【ヘッダー部】RGB24bitタイプのbmp(BitMap)形式ファイルのヘッダー部分
  PixelBuffer() As NTL_ColorRGB '【画素バッファ】RGB画素バッファ領域 (注)ヘッダー+画素バッファでbmpファイルを構成
  Nw As Long ' (x方向画素数×3に最も近い4の倍数) = (y方向に隣接する画素までのバイト距離)
  BasicColor As NTL_BasicColor '【基本色】固定色(白、黒、赤、オレンジ、黄、緑、シアン、青、紫、マゼンタ、茶、濃灰、灰、淡灰、濃緑)
  nColor As Long '【カラーインデックス】ColorLookupテーブルの登録色数
  Color() As NTL_ColorRGB '        ColorLookupテーブルのデータ領域
End Type

Public Type NTL3D_BitMapRGB ' ***** 3D描画/テクスチャーマッピング *****
  Header As NTL_BitMapRGB_Header '【ヘッダー部】RGB24bitタイプのbmp(BitMap)形式ファイルのヘッダー部分
  PixelBuffer() As NTL_ColorRGB '【画素バッファ】RGB画素バッファ領域 (注)ヘッダー+画素バッファでbmpファイルを構成
  Z_buffer() As Single '【Zバッファ】Zバッファ(単精度浮動小数点形式4bytes/画素)
  Nw As Long ' (x方向画素数×3に最も近い4の倍数) = (y方向に隣接する画素までのバイト距離)
  BasicColor As NTL_BasicColor '【基本色】固定色(白、黒、赤、オレンジ、黄、緑、シアン、青、紫、マゼンタ、茶、濃灰、灰、淡灰、濃緑)
  nColor As Long '【カラーインデックス】ColorLookupテーブルの登録色数
  Color() As NTL_ColorRGB '        ColorLookupテーブルのデータ領域
End Type


'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
'%%% 内部ルーチン
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' 以下の2つは内部で初期化に利用するルーチンです。
' 1) Windowsの1画素RGB24bitのbmpファイル形式は1画素当たり3バイト単位なので、
'  画像の横x方向サイズによってワード境界(4バイト単位)の問題が発生します。フ
'  ァイル格納時に1スキャンライン分のデータ量を調べるためにNTL_GetNwを使用し
'  ます。
' 2) 本プログラムではbmp形式オブジェクトへの描画の際に使用する色はNTL_ColorRGB
'  を使いますが、いちいち色を指定するのが面倒くさいので、予めオブジェクト内に
'  基本色を定義しておくようにしています。NTL_SetBasicColorはNTL2D_BitMapRGBや
'  NTL3D_BitMapRGBのBasicColorパレットに基本色を設定するためのルーチンです。


'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
'%%% x方向画素数×3に最も近い4の倍数を求める。(word境界対応)
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Private Function NTL_GetNw(Nx As Long) As Long
  Dim i As Long, j As Long

  i = Nx * 3&
  j = i Mod 4& '
  If j > 0 Then '
    i = (i \ 4& + 1&) * 4& '
  End If
  GetNw = i
End Function

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
'%%% 2D/3D描画オブジェクト内の基本色を設定する
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Public Sub NTL_SetBasicColor(BC As NTL_BasicColor)
  With BC '【基本色】 色   R G B
    .White.R = 255 ' 白 255 255 255
    .White.G = 255 '
    .White.B = 255 '
    .Black.R = 0 ' 黒 0 0 0
    .Black.G = 0 '
    .Black.B = 0 '
    .Red.R = 255 ' 赤 255 0 0
    .Red.G = 0 '
    .Red.B = 0 '
    .Yellow.R = 255 ' 黄 255 255 0
    .Yellow.G = 255 '
    .Yellow.B = 0 '
    .Green.R = 0 ' 緑 0 255 0
    .Green.G = 255 '
    .Green.B = 0 '
    .Cyan.R = 0 ' シアン 0 255 255
    .Cyan.G = 255 '
    .Cyan.B = 255 '
    .Blue.R = 0 ' 青 0 0 255
    .Blue.G = 0 '
    .Blue.B = 255 '
    .Magenta.R = 255 ' マゼンタ 255 0 255
    .Magenta.G = 0 '
    .Magenta.B = 255 '
    .Orange.R = 255 ' オレンジ 255 128 0
    .Orange.G = 128 '
    .Orange.B = 0 '
    .Violet.R = 128 ' 紫 128 0 255
    .Violet.G = 0 '
    .Violet.B = 128 '
    .Brown.R = 128 ' 茶 128 0 0
    .Brown.G = 0 '
    .Brown.B = 0 '
    .Gray.R = 128 ' 灰 128 128 128
    .Gray.G = 128 '
    .Gray.B = 128 '
    .DarkGray.R = 50 ' 濃灰  50 50 50
    .DarkGray.G = 50 '
    .DarkGray.B = 50 '
    .LightGray.R = 200 ' 淡灰 200 200 200
    .LightGray.G = 200 '
    .LightGray.B = 200 '
    .DarkGreen.R = 0 ' 濃緑  0 128 0
    .DarkGreen.G = 128 '
    .DarkGreen.B = 0 '
  End With
End Sub




'******************************************************
'**********
'********** 2次元グラッフィックス用コマンド
'**********
'******************************************************
' 本プログラムは3次元グラフィックスのためのライブラリですが、テクスチャーマッピング処理を行うために
' 2次元画像の取り扱いが必要なため、2次元グラフィックスも実装しています。点,線分,三角形の描画を行う
' ことができます。
'【2次元オブジェクトに対するコマンド】
' 1) NTL2D_CreateBitMapRGB_Header
'  1画素当たりRGB24bit, 横Nx画素, 縦Ny画素のbmp形式ファイルのヘッダー情報を生成するために使います。
' 2) NTL2D_GetBitMapFile
'  指定のRGB24bit形式のbmpファイルを読み込んで描画オブジェクトを生成します。BasicColor(基本色)パレット
'  が初期化されます。描画オブジェクトとなることでテクスチャーマッピングを行うことや、追加で描画を行うこと
'  ができるようになります。
' 3) NTL2D_BuildBitMap
'  指定サイズの2次元描画オブジェクトを生成します。BasicColor(基本色)パレットが初期化され、PixcelBuffer
' (フレーム)は白色でクリアされます。
' 4) NTL2D_FillBitMapImage
'  指定色でPixcelBufferをクリアします。NTL_ColorRGB形式で色の指定を行います。
' 5) NTL2D_CreateBitMapFile
'  指定描画オブジェクトの内容を指定ファイル名で1画素RGB24bitのbmp形式ファイルに書き出します。
' 6) NTL2D_InitializeColorLookupTable
'  描画オブジェクトにはBasicColor(基本色)パレットのほかにカラーインデックス方式の描画を行うために用意した
'  Colorパレット[配列]があります。このコマンドは指定色数の配列領域を確保するために用います。このコマンドは
'  領域の確保だけで、色の設定は行いません。
' 7) NTL2D_CreateColor
'  Colorパレット配列に色を直線補間処理で設定するために用います。開始番号、開始色、終了番号、終了色を指定する
'  ことで、指定2色間を補間したカラーパレットを作ることができます。
' 8) NTL2D_CreateGrayScale
'  指定した階調数のグレースケールをカラーパレットに設定することができます。
' 9) NTL2D_CreateDipoleScale
'  シミュレーションなどで正負の値を赤青系で階調表現したいときに使うと便利なカラーパレット設定コマンドです。
'  階調数を指定することができます。
' 10) NTL2D_CreateMonopoleScale
'  シミュレーションなどで絶対強度を階調表現したいときに使うと便利なカラーパレット設定コマンドです。
'  階調数を指定することができます。
' 11) MMD2D_DrawPixel
'  1画素単位に描画するコマンドです。
'  MikuMikuDanceではuv空間の座標範囲は0.0~1.0です。またRGB項は0.0~1.0の範囲で指定されているので、自動的に
'  u座標を0~Nx-1に、v座標を0~Ny-1に、RGB項を0~255になるように変換して描画します。
' 12) MMD2D_DrawLineConstant
'  指定uv座標2点間を結ぶ線分を指定単色で描画するコマンドです。
'  MikuMikuDanceではuv空間の座標範囲は0.0~1.0です。またRGB項は0.0~1.0の範囲で指定されているので、自動的に
'  u座標を0~Nx-1に、v座標を0~Ny-1に、RGB項を0~255になるように変換して描画します。
' 13) MMD2D_DrawLineShading
'  指定uv座標2点間を結ぶ線分を指定2色の直線補間をかけながら描画するコマンドです。
'  MikuMikuDanceではuv空間の座標範囲は0.0~1.0です。またRGB項は0.0~1.0の範囲で指定されているので、自動的に
'  u座標を0~Nx-1に、v座標を0~Ny-1に、RGB項を0~255になるように変換して描画します。
' 14) NTL2D_DrawPixel
'  指定色(NTL_ColorRGB)で画素を描画します。uv座標は画素に対応しており、PixelBufferのu座標の範囲は0~(Nx-1)、
'  v座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のuv座標を指定した場合には描画されないだけで問題あり
'  ません。
' 15) NTL2D_DrawLineConstant
'  指定単色(NTL_ColorRGB)で指定2点間を結ぶ線分を描画します。uv座標は画素に対応しており、PixelBufferのu座標の
'  範囲は0~(Nx-1)、v座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のuv座標を指定した場合にはまずPixel
'  Bufferの枠に交差するかチェックし、枠に交差しない線分は描画されません。枠に交差する線分の場合は枠内の点だけが
'  描画されます。但し、これはクリッピング処理ではなく、描画時に有効な画素のみを描画する方式です。
' 16) NTL2D_DrawLineShading
'  指定2色(NTL_ColorRGB)間を直線補間しながら指定2点間を結ぶ線分を描画します。uv座標は画素に対応しており、
'  PixelBufferのu座標の範囲は0~(Nx-1)、v座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のuv座標を指定
'  した場合にはまずPixel Bufferの枠に交差するかチェックし、枠に交差しない線分は描画されません。枠に交差する
'  線分の場合は枠内の点だけが描画されます。但し、これはクリッピング処理ではなく、描画時に有効な画素のみを描画
'  する方式です。
' 17) NTL2D_LineKerning
'  指定線分がPixelBufferのフレーム(枠)内あるいは枠に交差して描画処理可能であるか、あるいは枠外に存在する線分で
'  描画の必要性がないかを判断する関数である。この関数の戻り値がtrueならカーニング可能な(描画の必要性がない)線分
'  であることを示す。
' 18) NTL2D_Triangle
'  カラーシェーディングに対応した三角形描画を行う。uv座標は浮動小数点形式であるが、3線分で囲まれる整数座標上の
'  画素のみを描画する。カラーも整数座標上の色が計算される。
' 19) NTL2D_Polygon
'  n頂点からなる多角形を描画する。[NTL2D_Triangleで使用]
' 20) NTL2D_ScanLine
'  スキャンラインに沿って2点間をカラーを直線補間しながら描画する。[NTL2D_Polygonで使用]
'  
'  
'  

'###########################################################
'### 2D描画オブジェクトのヘッダー情報を指定イメージサイズで構成する
'###########################################################
' 横Nx画素、縦Ny画素の2次元ビットマップ・オブジェクトのヘッダー情報を作成する。
Private Sub NTL2D_CreateBitMapRGB_Header(cg As NTL2D_BitMapRGB, Nx As Long, Ny As Long)
  cg.Nw = NTL_GetNw(Nx)
  With cg.Header
    .ID = "BM"
    .FileLength = 54& + cg.Nw * Ny
    .Null1 = 0&
    .HeaderSize = 54
    .Offset = 40
    .Nx = Nx
    .Ny = Ny
    .NumberOfPlanes = 1
    .BitsOfPixel = 24
    .Null2 = 0&
    .SizeOfData = cg.Nw * Ny
    .Null3(0) = 0&
    .Null3(1) = 0&
    .Null3(2) = 0&
    .Null3(3) = 0&
  End With
End Sub

'###########################################################
'### 指定画像ファイル(RGB24bit BMP形式)を2D形式として読み込む
'###########################################################
' RGB24bit BMP形式の画像ファイルを読込、オブジェクトを作成する。
Public Sub NTL2D_GetBitMapFile(cg As NTL2D_BitMapRGB, filename As String)
  Open filename For Binary Access Read As #1
    Get #1, , cg.Header
    ReDim cg.PixelBuffer(cg.Header.Nx - 1, cg.Header.Ny - 1)
    Get #1, 55, cg.PixelBuffer
  Close #1
  cg.Nw = NTL_GetNw(cg.Header.Nx) '【x方向画素数×3に最も近い4の倍数を求める】
  NTL_SetBasicColor cg.BasicColor '【基本色を設定する】
End Sub

'###########################################################
'### 指定サイズの画像ファイル(RGB24bit BMP形式)を2D形式で作る
'###########################################################
Public Sub NTL2D_BuildBitMap(Nx As Long, Ny As Long, cg As NTL2D_BitMapRGB)
  NTL2D_CreateBitMapRGB_Header cg, Nx, Ny '【BMP形式ファイルのヘッダーを作成する】
  NTL_SetBasicColor cg.BasicColor '【基本色を設定する】
  ReDim cg.PixelBuffer(cg.Header.Nx - 1, cg.Header.Ny - 1) '【画素バッファの初期化】
  NTL2D_FillBitMapImage cg, cg.BasicColor.White '
End Sub

'###########################################################
'### 2D用画素バッファのカレントイメージをクリアする
'###########################################################
Public Sub NTL2D_FillBitMapImage(cg As NTL2D_BitMapRGB, P As NTL_ColorRGB)
  Dim i As Long, j As Long

  For j = 0 To cg.Header.Ny - 1
    For i = 0 To cg.Header.Nx - 1
      cg.PixelBuffer(i, j) = P
    Next i
  Next j
End Sub

'###########################################################
'### 2D用カレントイメージをファイル化する
'###########################################################
Public Sub NTL2D_CreateBitMapFile(cg As NTL2D_BitMapRGB, filename As String)
  Open filename For Binary Access Write As #1
    Put #1, 1, cg.Header
    Put #1, 55, cg.PixelBuffer
  Close #1
End Sub

'###########################################################
'### 2D用カラーLookupテーブルを初期化する
'###########################################################
Public Sub NTL2D_InitializeColorLookupTable(cg As NTL2D_BitMapRGB, n As Long)
  cg.nColor = n
  ReDim cg.Color(n - 1)
End Sub

'###########################################################
'### 2D用カラーパレットの指定色間を補間した色を作る
'###########################################################
Public Sub NTL2D_CreateColor(cg As NTL2D_BitMapRGB, i1 As Long, P1 As NTL_ColorRGB, i2 As Long, P2 As NTL_ColorRGB)
  Dim Dr As Single, Dg As Single, Db As Single, i As Long

  Dr = (CSng(P2.R) - CSng(P1.R)) / CSng(i2 - i1)
  Dg = (CSng(P2.G) - CSng(P1.G)) / CSng(i2 - i1)
  Db = (CSng(P2.B) - CSng(P1.B)) / CSng(i2 - i1)
  For i = i1 To i2 Step Sgn(i2 - i1)
    cg.Color(i).R = P1.R + Dr * CSng(i - i1)
    cg.Color(i).G = P1.G + Dg * CSng(i - i1)
    cg.Color(i).B = P1.B + Db * CSng(i - i1)
  Next i
End Sub

'###########################################################
'### 2D用カラーパレットを作る(グレイ・スケール)
'###########################################################
Public Sub NTL2D_CreateGrayScale(cg As NTL2D_BitMapRGB, n As Long)
  NTL2D_InitializeColorLookupTable cg, n
  NTL2D_CreateColor cg, 0, cg.BasicColor.Black, n - 1, cg.BasicColor.White
End Sub

'###########################################################
'### 2D用カラーパレットを作る(正負)
'###########################################################
Public Sub NTL2D_CreateDipoleScale(cg As NTL2D_BitMapRGB, n As Long)
  Dim C0 As Long, C1 As Long, C2 As Long, C3 As Long, C4 As Long, C5 As Long

  C0 = n - 1
  C1 = n * 0.84
  C2 = n * 0.67
  C3 = n * 0.5
  C4 = n * 0.33
  C5 = n * 0.17
  NTL2D_InitializeColorLookupTable cg, n
  NTL2D_CreateColor cg, C0, cg.BasicColor.Red, C1, cg.BasicColor.Orange
  NTL2D_CreateColor cg, C1, cg.BasicColor.Orange, C2, cg.BasicColor.Yellow
  NTL2D_CreateColor cg, C2, cg.BasicColor.Yellow, C3, cg.BasicColor.Green
  NTL2D_CreateColor cg, C3, cg.BasicColor.DarkGreen, C4, cg.BasicColor.Cyan
  NTL2D_CreateColor cg, C4, cg.BasicColor.Cyan, C5, cg.BasicColor.Blue
  NTL2D_CreateColor cg, C5, cg.BasicColor.Blue, 0, cg.BasicColor.Violet
  cg.Color(C3) = cg.BasicColor.White
End Sub

'###########################################################
'### 2D用カラーパレットを作る(絶対値)
'###########################################################
Public Sub NTL2D_CreateMonopoleScale(cg As NTL2D_BitMapRGB, n As Long)
  Dim C0 As Long, C1 As Long, C2 As Long, C3 As Long, C4 As Long, C5 As Long, C6 As Long

  C0 = n - 1
  C1 = n * 10 / 12
  C2 = n * 8 / 12
  C3 = n * 6 / 12
  C4 = n * 5 / 12
  C5 = n * 4 / 12
  C6 = n * 2 / 12
  NTL2D_InitializeColorLookupTable cg, n
  ' 表示色数 赤⇒橙⇒黄⇒緑⇒淡青⇒青⇒濃灰⇒灰⇒淡灰⇒白
  NTL2D_CreateColor cg, C0, cg.BasicColor.Red, C1, cg.BasicColor.Orange
  NTL2D_CreateColor cg, C1, cg.BasicColor.Orange, C2, cg.BasicColor.Yellow
  NTL2D_CreateColor cg, C2, cg.BasicColor.Yellow, C3, cg.BasicColor.Green
  NTL2D_CreateColor cg, C3, cg.BasicColor.Green, C4, cg.BasicColor.DarkGreen
  NTL2D_CreateColor cg, C4, cg.BasicColor.DarkGreen, C5, cg.BasicColor.Blue
  NTL2D_CreateColor cg, C5, cg.BasicColor.Blue, C6, cg.BasicColor.Violet
  NTL2D_CreateColor cg, C6, cg.BasicColor.DarkGray, 0, cg.BasicColor.White
End Sub

'###########################################################
'###【MMD】2Dカレントイメージに指定色で点を描画する
'###########################################################
Public Sub MMD2D_DrawPixel(cg As NTL2D_BitMapRGB, MMD_uv As x_Coords2d, MMD_P As x_ColorRGBA)
  Dim P As NTL_ColorRGB, uv As x_Coords2d

  P.R = (255 * MMD_P.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P.G = (255 * MMD_P.Green) And &HFF '
  P.B = (255 * MMD_P.Blue) And &HFF '
  uv.u = (MMD_uv.u - Int(MMD_uv.u)) * (cg.Header.Nx - 1) '【uv座標変換】テクスチャの回込み処理も含む
  uv.v = (MMD_uv.v - Int(MMD_uv.v)) * (cg.Header.Ny - 1)
  NTL2D_DrawPixel cg, uv, P
End Sub

'###########################################################
'###【MMD】2Dカレントイメージに指定単色で直線を描画する(コンスタント・シェーディング)
'###########################################################
Public Sub MMD2D_DrawLineConstant(cg As NTL2D_BitMapRGB, MMD_uv1 As x_Coords2d, MMD_uv2 As x_Coords2d, MMD_P As x_ColorRGBA)
  Dim P As NTL_ColorRGB, uv1 As x_Coords2d, uv2 As x_Coords2d

  P.R = (255 * MMD_P.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P.G = (255 * MMD_P.Green) And &HFF '
  P.B = (255 * MMD_P.Blue) And &HFF '
  uv1.u = (MMD_uv1.u - Int(MMD_uv1.u)) * (cg.Header.Nx - 1) '【uv座標変換】テクスチャの回込み処理も含む
  uv1.v = (MMD_uv1.v - Int(MMD_uv1.v)) * (cg.Header.Ny - 1)
  uv2.u = (MMD_uv2.u - Int(MMD_uv2.u)) * (cg.Header.Nx - 1) '【uv座標変換】テクスチャの回込み処理も含む
  uv2.v = (MMD_uv2.v - Int(MMD_uv2.v)) * (cg.Header.Ny - 1)
  NTL2D_DrawLineShading cg, uv1, P, uv2, P
End Sub

'###########################################################
'###【MMD】 2Dカレントイメージに指定補間色で直線を描画する(カラー・シェーディング)
'###########################################################
Public Sub MMD2D_DrawLineShading(cg As NTL2D_BitMapRGB, MMD_uv1 As x_Coords2d, MMD_P1 As x_ColorRGBA, MMD_uv2 As x_Coords2d, MMD_P2 As x_ColorRGBA)
  Dim P1 As NTL_ColorRGB, P2 As NTL_ColorRGB, uv1 As x_Coords2d, uv2 As x_Coords2d

  P1.R = (255 * MMD_P1.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P1.G = (255 * MMD_P1.Green) And &HFF '
  P1.B = (255 * MMD_P1.Blue) And &HFF '
  P2.R = (255 * MMD_P2.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P2.G = (255 * MMD_P2.Green) And &HFF '
  P2.B = (255 * MMD_P2.Blue) And &HFF '
  uv1.u = (MMD_uv1.u - Int(MMD_uv1.u)) * (cg.Header.Nx - 1) '【uv座標変換】テクスチャの回込み処理も含む
  uv1.v = (MMD_uv1.v - Int(MMD_uv1.v)) * (cg.Header.Ny - 1)
  uv2.u = (MMD_uv2.u - Int(MMD_uv2.u)) * (cg.Header.Nx - 1) '【uv座標変換】テクスチャの回込み処理も含む
  uv2.v = (MMD_uv2.v - Int(MMD_uv2.v)) * (cg.Header.Ny - 1)
  NTL2D_DrawLineShading cg, uv1, P1, uv2, P2
End Sub

'###########################################################
'###【NTL】2Dカレントイメージに指定色で点を描画する[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL2D_DrawPixel(cg As NTL2D_BitMapRGB, uv As x_Coords2d, P As NTL_ColorRGB) ' テクスチャーの折返し処理は上位コマンドで行う
  If (CLng(uv.u) >= 0 And CLng(uv.u) < cg.Header.Nx And CLng(uv.v) >= 0 And CLng(uv.v) < cg.Header.Ny) Then
    cg.PixelBuffer(CLng(uv.u), CLng(uv.v)) = P
  End If
End Sub

'###########################################################
'###【NTL】2Dカレントイメージに指定単色で直線を描画する(コンスタント・シェーディング)[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL2D_DrawLineConstant(cg As NTL2D_BitMapRGB, uv1 As x_Coords2d, uv2 As x_Coords2d, P As NTL_ColorRGB)
  NTL2D_DrawLineShading cg, uv1, P, uv2, P
End Sub

'###########################################################
'###【NTL】2Dカレント・イメージに指定補間色で直線を描画する(カラー・シェーディング)[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL2D_DrawLineShading(cg As NTL2D_BitMapRGB, uv1 As x_Coords2d, P1 As NTL_ColorRGB, uv2 As x_Coords2d, P2 As NTL_ColorRGB)
  Dim Du As Single, Dv As Single, Dr As Single, Dg As Single, Db As Single, w As Single, MajorDir As Long
  Dim i As Long, j As Long

  If NTL2D_LineKerning(cg, uv1, uv2) Then GoTo LE ' この線分が表示枠に引っ掛からない場合、無処理。
  Du = uv2.u - uv1.u
  Dv = uv2.v - uv1.v
  If Abs(Du) > Abs(Dv) Then ' u座標がMajor軸(長軸)
    If Du < 0 Then MajorDir = -1 Else MajorDir = 1 ' Major軸の補間進行方向を示す
    If CLng(Du) <> 0 Then
      w = 1# / Du
    Else
      w = 0
    End If
    Dv = Dv * w
    Dr = CSng(P2.R - P1.R) * w
    Dg = CSng(P2.G - P1.G) * w
    Db = CSng(P2.B - P1.B) * w
    For i = CLng(uv1.u) To CLng(uv2.u) Step MajorDir
      If (i >= 0 And i < cg.Header.Nx) Then
        j = uv1.v + Dv * CSng(i - CLng(uv1.u))
        If (j >= 0 And j < cg.Header.Ny) Then
          cg.PixelBuffer(i, j).R = P1.R + Dr * CSng(i)
          cg.PixelBuffer(i, j).G = P1.G + Dg * CSng(i)
          cg.PixelBuffer(i, j).B = P1.B + Db * CSng(i)
        End If
      End If
    Next i
  Else ' v座標がMajor軸(長軸)
    If Dv < 0 Then MajorDir = -1 Else MajorDir = 1 ' Major軸の補間進行方向を示す
    If CLng(Dv) <> 0 Then
      w = 1# / Dv
    Else
      w = 0
    End If
    Du = Du * w
    Dr = CSng(P2.R - P1.R) * w
    Dg = CSng(P2.G - P1.G) * w
    Db = CSng(P2.B - P1.B) * w
    For j = CLng(uv1.v) To CLng(uv2.v) Step MajorDir
       If (j >= 0 And j < cg.Header.Ny) Then
        i = uv1.u + Du * CSng(j - CLng(uv1.v))
        If (i >= 0 And i < cg.Header.Nx) Then
          cg.PixelBuffer(i, j).R = P1.R + Dr * CSng(j)
          cg.PixelBuffer(i, j).G = P1.G + Dg * CSng(j)
          cg.PixelBuffer(i, j).B = P1.B + Db * CSng(j)
        End If
      End If
    Next j
  End If
LE:
End Sub

'###########################################################
'###【NTL】2Dカレント・イメージに指定補間色で直線を描画する(カーニング) 枠に引っ掛かったときに線分をクリッピング処理していない。チェックのみ。
'###########################################################
Public Function NTL2D_LineKerning(cg As NTL2D_BitMapRGB, uv1 As x_Coords2d, uv2 As x_Coords2d) As Boolean
  Dim u As Single, v As Single, Duv1 As Single, Duv2 As Single, flag1 As Boolean, flag2 As Boolean

  NTL2D_LineClipping = False
  flag1 = (uv1.u >= 0) And (uv1.u < cg.Header.Nx) And (uv1.v >= 0) And (uv1.v < cg.Header.Ny) ' Trueなら頂点uv1は表示枠内
  flag2 = (uv2.u >= 0) And (uv2.u < cg.Header.Nx) And (uv2.v >= 0) And (uv2.v < cg.Header.Ny) ' Trueなら頂点uv2は表示枠内
  If (flag1 Or flag2) Then GoTo LE '【2頂点のいずれか一方が表示枠内】カーニング不可
  If (uv1.u = uv2.u) Or (uv1.v = uv2.v) Then GoTo LE
  Duv1 = (uv2.u - uv1.u) / (uv2.v - uv1.v)
  Duv2 = 1# / Duv1
  '【交差チェック】v軸(v=0)及びv=cg.Header.Ny-1
  ' u = u1 + (v-v1)*(u2-u1)/(v2-v1)
  u = uv1.u - uv1.v * Duv1
  If (u >= 0 And u < cg.Header.Nx) Then GoTo LE '【線分交差】枠下側 v=0
  u = uv1.u + (cg.Header.Ny - uv1.v) * Duv1
  If (u >= 0 And u < cg.Header.Nx) Then GoTo LE '【線分交差】枠上側 v=cg.Header.Ny
  '【交差チェック】u軸(u=0)及びu=cg.Header.Nx-1
  ' v = v1 + (u-u1)(v2-v1)/(u2-u1)
  v = uv1.v - uv1.u * Duv2
  If (v >= 0 And v < cg.Header.Ny) Then GoTo LE '【線分交差】枠左側 u=0
  v = uv1.v + (cg.Header.Nx - uv1.u) * Duv2
  If (v >= 0 And v < cg.Header.Ny) Then GoTo LE '【線分交差】枠右側 u=cg.Header.Nx
  NTL2D_LineClipping = True '【2頂点とも表示枠外、かつ表示枠と交差しない⇒カーニング】
LE:
End Function

'###########################################################
'###【NTL】2Dカレント・イメージに指定補間色で三角形を描画する
'###########################################################
Public Sub NTL2D_Triangle(cg As NTL2D_BitMapRGB, uv1 As x_Coords2d, P1 As NTL_ColorRGB, uv2 As x_Coords2d, P2 As NTL_ColorRGB, uv3 As x_Coords2d, P3 As NTL_ColorRGB)
  Dim uv(2) As x_Coords2d, P(2) As NTL_ColorRGB
  uv(0) = uv1: uv(1) = uv2: uv(2) = uv3: P(0) = P1: P(1) = P2: P(2) = P3
  NTL2D_Polygon cg, 3, uv, P
End Sub


'###########################################################
'###【NTL】2Dカレント・イメージに指定補間色で任意多角形を描画する
'###########################################################
Public Sub NTL2D_Polygon(cg As NTL2D_BitMapRGB, n As Long, uv() As x_Coords2d, pn() As NTL_ColorRGB)
  Dim Ymax As Single, Ymin As Single, Pmax As Long, Pmin As Long
  Dim uva1 As x_Coords2d, uva2 As x_Coords2d, uvb1 As x_Coords2d, uvb2 As x_Coords2d
  Dim pa1 As x_ColorRGBA, pa2 As x_ColorRGBA, pb1 As x_ColorRGBA, pb2 As x_ColorRGBA
  Dim Dua As Single, Dra As Single, Dga As Single, Dba As Single, Dub As Single, Drb As Single, Dgb As Single, Dbb As Single
  Dim i As Long, Pa As Long, Pb As Long, Ca As Long, Cb As Long, wa As Single, wb As Single, w As Single
  Dim P() As x_ColorRGBA

  ReDim P(n - 1)
  Ymax = -NTL3D_Z_max: Ymin = NTL3D_Z_max
  For i = 0 To n - 1
    P(i).Red = pn(i).R
    P(i).Green = pn(i).G
    P(i).Blue = pn(i).B
    P(i).Alpha = 1
    If (uv(i).v > Ymax) Then
      Pmax = i: Ymax = uv(i).v
    End If
    If (uv(i).v < Ymin) Then
      Pmin = i: Ymin = uv(i).v
    End If
  Next i
  i = 0: Pa = Pmax: Pb = Pmax: Ca = 0: Cb = 0
GetALine:
  uva1 = uv(Pa): pa1 = P(Pa): Pa = Pa + 1: If Pa = n Then Pa = 0
  uva2 = uv(Pa): pa2 = P(Pa): wa = uva2.v - uva1.v: If Abs(wa) = 0 Then wa = 0 Else wa = 1# / wa
  Dua = (uva2.u - uva1.u) * wa: Dra = (pa2.Red - pa1.Red) * wa: Dga = (pa2.Green - pa1.Green) * wa: Dba = (pa2.Blue - pa1.Blue) * wa
  w = uva1.v - Int(uva1.v): uva1.v = Int(uva1.v): Ca = Abs(uva2.v - uva1.v)
  uva1.u = uva1.u - Dua * w: pa1.Red = pa1.Red - Dra * w: pa1.Green = pa1.Green - Dga * w: pa1.Blue = pa1.Blue - Dba * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L3
GetBLine:
  uvb1 = uv(Pb): pb1 = P(Pb): Pb = Pb - 1: If Pb = -1 Then Pb = n - 1
  uvb2 = uv(Pb): pb2 = P(Pb): wb = uvb2.v - uvb1.v: If Abs(wb) = 0 Then wb = 0 Else wb = 1# / wb
  Dub = (uvb2.u - uvb1.u) * wb: Drb = (pb2.Red - pb1.Red) * wb: Dgb = (pb2.Green - pb1.Green) * wb: Dbb = (pb2.Blue - pb1.Blue) * wb
  w = uvb1.v - Int(uvb1.v): uvb1.v = Int(uvb1.v): Cb = Abs(uvb2.v - uvb1.v)
  uvb1.u = uvb1.u - Dub * w: pb1.Red = pb1.Red - Drb * w: pb1.Green = pb1.Green - Dgb * w: pb1.Blue = pb1.Blue - Dbb * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L4
  i = 1
L1:
  If (Ca < 0) And (Pa <> Pb) Then GoTo GetALine '【20090928修正】
  If (Cb < 0) And (Pa <> Pb) Then GoTo GetBLine '【20090928修正】
  If (Ca <= 0) And (Cb <= 0) And (Pa = Pb) Then GoTo LE '【20090928修正】
L2:
  NTL2D_ScanLine cg, uva1, pa1, uvb1, pb1
' ax = uva1.u: bx = uvb1.u: ay = uva1.v: by = uvb1.v '【Debug用】
L3:
  Ca = Ca - 1: uva1.v = uva1.v - 1: uva1.u = uva1.u - Dua: pa1.Red = pa1.Red - Dra: pa1.Green = pa1.Green - Dga: pa1.Blue = pa1.Blue - Dba
  If (Ca < 0) And (Pa <> Pb) Then '【20090928修正】
    i = 2: GoTo GetALine
  End If
L4:
  Cb = Cb - 1: uvb1.v = uvb1.v - 1: uvb1.u = uvb1.u - Dub: pb1.Red = pb1.Red - Drb: pb1.Green = pb1.Green - Dgb: pb1.Blue = pb1.Blue - Dbb
  If (Cb < 0) And (Pa <> Pb) Then '【20090928修正】
i = 2: GoTo GetBLine
  End If
  GoTo L1
LE:
End Sub

'###########################################################
'###【NTL】2Dカレント・イメージに指定補間色でスキャンラインを描画する
'###########################################################
Public Sub NTL2D_ScanLine(cg As NTL2D_BitMapRGB, uv1 As x_Coords2d, P1 As x_ColorRGBA, uv2 As x_Coords2d, P2 As x_ColorRGBA)
  Dim uvs As x_Coords2d, ps As x_ColorRGBA, uve As x_Coords2d, pe As x_ColorRGBA, NTL_P As NTL_ColorRGB
  Dim Du As Single, Dr As Single, Dg As Single, Db As Single, w As Single, u As Single, i As Long, j As Long

  If (uv1.u < uv2.u) Then '【スキャンラインを左から右へ描画するために始点と終点を決定する】
    uvs = uv1: uve = uv2: ps = P1: pe = P2
  Else
    uvs = uv2: uve = uv1: ps = P2: pe = P1
  End If
  If uve.u < 0 Then GoTo LE '【終点がu=0より小さいので終了】
  Du = uve.u - uvs.u: If Du <> 0 Then w = 1# / Du Else w = 0
  Dr = (pe.Red - ps.Red) * w
  Dg = (pe.Green - ps.Green) * w
  Db = (pe.Blue - ps.Blue) * w
  If uvs.u >= 0 Then '【20090928修正】
    u = uvs.u - Int(uvs.u) ' u軸方向微調整分(1画素未満)
    If u <> 0 Then u = 1 - u '
    If u > Du Then GoTo LE ' 幅を超えているので終了
    uvs.u = uvs.u + u ' 整数座標に合わせる
  Else '
    u = -uvs.u ' u軸方向スキップ分
    If u > Du Then GoTo LE ' 幅を超えているので終了
    uvs.u = 0 '
  End If '
  ps.Red = ps.Red + u * Dr
  ps.Green = ps.Green + u * Dg
  ps.Blue = ps.Blue + u * Db
  j = CLng(uve.u - uvs.u) '【20090928修正】
  For i = 0 To j
    NTL_P.R = CInt(ps.Red) And &HFF
    NTL_P.G = CInt(ps.Green) And &HFF
    NTL_P.B = CInt(ps.Blue) And &HFF
    NTL2D_DrawPixel cg, uvs, NTL_P
    If i = j Then GoTo LE
    uvs.u = uvs.u + 1
    ps.Red = ps.Red + Dr
    ps.Green = ps.Green + Dg
    ps.Blue = ps.Blue + Db
  Next i
LE:
End Sub









'******************************************************
'**********
'********** 3次元グラッフィックス用コマンド
'**********
'******************************************************
' 本プログラムはテクスチャーマッピング処理も実装した3次元グラフィックスのためのライブラリです。
' 点,線分,三角形の描画を行うことができます。
'【2次元オブジェクトに対するコマンド】
' 1) NTL3D_CreateBitMapRGB_Header
'  3次元描画オブジェクト型NTL3D_BitMapRGBに対して、1画素当たりRGB24bit, 横Nx画素, 縦Ny画素のbmp形式ファイル
'  のヘッダー情報を生成するために使います。
' 2) NTL3D_GetBitMapFile
'  指定のRGB24bit形式のbmpファイルを読み込んで3次元描画オブジェクト型NTL3D_BitMapRGBを生成します。BasicColor
'  (基本色)パレットが初期化され、zバッファが最奥値に初期化されます。描画オブジェクトとなることでテクスチャー
'  マッピングを行うことや、追加で描画を行うことができるようになります。
' 3) NTL3D_BuildBitMap
'  指定サイズの3次元描画オブジェクト型NTL3D_BitMapRGBを生成します。BasicColor(基本色)パレットが初期化され、
'  PixcelBuffer(フレーム)は白色でクリアされ、zバッファが最奥値に初期化されます。
' 4) NTL3D_FillBitMapImage
'  3次元描画オブジェクト型NTL3D_BitMapRGBのPixcelBufferを指定色でクリアします。NTL_ColorRGB形式で色の指定を行います。
' 5) NTL3D_ClearZ_Buffer
'  3次元描画オブジェクト型NTL3D_BitMapRGBのZ_bufferを最奥値でクリアします。z値は単精度浮動小数点形式です。
' 6) NTL3D_CreateBitMapFile
'  指定描画オブジェクトの内容を指定ファイル名で1画素RGB24bitのbmp形式ファイルに書き出します。
' 7) NTL3D_InitializeColorLookupTable
'  指定3次元描画オブジェクト型NTL3D_BitMapRGBにはBasicColor(基本色)パレットのほかにカラーインデックス方式の描画を行うため
'  に用意したColorパレット[配列]があります。このコマンドは指定色数の配列領域を確保するために用います。このコマンドは領域
'  の確保だけで、色の設定は行いません。
' 8) NTL3D_CreateColor
'  Colorパレット配列に色を直線補間処理で設定するために用います。開始番号、開始色、終了番号、終了色を指定する
'  ことで、指定2色間を補間したカラーパレットを作ることができます。
' 9) NTL3D_CreateGrayScale
'  指定した階調数のグレースケールをカラーパレットに設定することができます。
' 10) NTL3D_CreateDipoleScale
'  シミュレーションなどで正負の値を赤青系で階調表現したいときに使うと便利なカラーパレット設定コマンドです。
'  階調数を指定することができます。
' 11) NTL3D_CreateMonopoleScale
'  シミュレーションなどで絶対強度を階調表現したいときに使うと便利なカラーパレット設定コマンドです。
'  階調数を指定することができます。
' 12) MMD3D_DrawPixel
'  1画素単位に描画するコマンドです。zバッファは後書き優先になっています。
'  MikuMikuDanceではRGB項は0.0~1.0の範囲で指定されているので、自動的にRGB項を0~255になるように変換して描画します。
' 13) MMD3D_DrawLineConstant
'  指定xyz座標2点間を結ぶ線分を指定単色で描画するコマンドです。zバッファは後書き優先になっています。
'  MikuMikuDanceではRGB項は0.0~1.0の範囲で指定されているので、自動的にRGB項を0~255になるように変換して描画します。
' 14) MMD3D_DrawLineShading
'  指定xyz座標2点間を結ぶ線分を指定2色の直線補間をかけながら描画するコマンドです。zバッファは後書き優先になっています。
'  MikuMikuDanceではRGB項は0.0~1.0の範囲で指定されているので、自動的にRGB項を0~255になるように変換して描画します。
' 15) NTL3D_DrawPixel
'  指定色(NTL_ColorRGB)で画素を描画します。x,y座標は画素に対応しており、PixelBufferのx座標の範囲は0~(Nx-1)、
'  y座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のxy座標を指定した場合には描画されないだけで問題あり
'  ません。zバッファは後書き優先になっています。
' 16) NTL3D_DrawLineConstant
'  指定単色(NTL_ColorRGB)で指定2点間を結ぶ線分を描画します。x,y座標は画素に対応しており、PixelBufferのx座標の
'  範囲は0~(Nx-1)、y座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のxy座標を指定した場合にはまずPixel
'  Bufferの枠に交差するかチェックし、枠に交差しない線分は描画されません。枠に交差する線分の場合は枠内の点だけが
'  描画されます。但し、これはクリッピング処理ではなく、描画時に有効な画素のみを描画する方式です。
'  zバッファは後書き優先になっています。
' 17) NTL3D_DrawLineShading
'  指定2色(NTL_ColorRGB)間を直線補間しながら指定2点間を結ぶ線分を描画します。x,y座標は画素に対応しており、
'  PixelBufferのx座標の範囲は0~(Nx-1)、y座標の範囲は0~(Ny-1)の単精度浮動小数点形式です。枠外のxy座標を指定
'  した場合にはまずPixel Bufferの枠に交差するかチェックし、枠に交差しない線分は描画されません。枠に交差する
'  線分の場合は枠内の点だけが描画されます。但し、これはクリッピング処理ではなく、描画時に有効な画素のみを描画
'  する方式です。zバッファは後書き優先になっています。
' 18) NTL3D_LineKerning
'  指定線分がPixelBufferのフレーム(枠)内あるいは枠に交差して描画処理可能であるか、あるいは枠外に存在する線分で
'  描画の必要性がないかを判断する関数である。この関数の戻り値がtrueならカーニング可能な(描画の必要性がない)線分
'  であることを示す。
' 19) NTL3D_Triangle
'  カラーシェーディングに対応した三角形描画を行う。xyz座標は浮動小数点形式であるが、3線分で囲まれる整数座標上の
'  画素のみを描画する。カラーも整数座標上の色が計算される。
' 20) NTL3D_Polygon
'  n頂点からなる多角形を描画する。[NTL3D_Triangleで使用]
' 21) NTL3D_ScanLine
'  スキャンラインに沿って2点間をカラーを直線補間しながら描画する。[NTL3D_Polygonで使用]
' 22) NTL3D_TriangleWithTextureMapping
'  指定uv空間の三角形領域内のテクスチャーを指定xyz空間の三角形領域にマッピングする。xyz座標、uv座標は画素単位の浮動
'  小数点形式である。xy座標において3線分で囲まれる整数座標上の画素のみを描画する。カラーも整数座標上の色が計算される。
' 23) NTL3D_PolygonWithTextureMapping
'  n頂点からなる多角形に対してテクスチャーマッピングを行う。
' 24) NTL3D_ScanLineWithTextureMapping
'  スキャンラインに沿って2点間のテクスチャーマッピングを行う。




'###########################################################
'### 3D描画オブジェクトのヘッダー情報を指定イメージサイズで構成する
'###########################################################
Private Sub NTL3D_CreateBitMapRGB_Header(cg As NTL3D_BitMapRGB, Nx As Long, Ny As Long)
  cg.Nw = NTL_GetNw(Nx)
  With cg.Header
    .ID = "BM"
    .FileLength = 54& + cg.Nw * Ny
    .Null1 = 0&
    .HeaderSize = 54
    .Offset = 40
    .Nx = Nx
    .Ny = Ny
    .NumberOfPlanes = 1
    .BitsOfPixel = 24
    .Null2 = 0&
    .SizeOfData = cg.Nw * Ny
    .Null3(0) = 0&
    .Null3(1) = 0&
    .Null3(2) = 0&
    .Null3(3) = 0&
  End With
End Sub

'###########################################################
'### 指定画像ファイル(RGB24bit BMP形式)を3D形式として読み込む
'###########################################################
Public Sub NTL3D_GetBitMapFile(cg As NTL3D_BitMapRGB, filename As String)
  Open filename For Binary Access Read As #1
    Get #1, , cg.Header
    ReDim cg.PixelBuffer(cg.Header.Nx - 1, cg.Header.Ny - 1)
    Get #1, 55, cg.PixelBuffer
  Close #1
  ReDim cg.Z_buffer(cg.Header.Nx - 1, cg.Header.Ny - 1) '【zバッファの初期化】
  NTL3D_ClearZ_Buffer cg, NTL3D_Z_max '
  cg.Nw = NTL_GetNw(cg.Header.Nx) '【x方向画素数×3に最も近い4の倍数を求める】
  NTL_SetBasicColor cg.BasicColor '【基本色を設定する】
End Sub

'###########################################################
'### 指定サイズの画像ファイル(RGB24bit BMP形式)を3D形式で作る
'###########################################################
Public Sub NTL3D_BuildBitMap(Nx As Long, Ny As Long, cg As NTL3D_BitMapRGB)
  NTL3D_CreateBitMapRGB_Header cg, Nx, Ny '【BMP形式ファイルのヘッダーを作成する】
  NTL_SetBasicColor cg.BasicColor '【基本色を設定する】
  ReDim cg.PixelBuffer(cg.Header.Nx - 1, cg.Header.Ny - 1) '【画素バッファの初期化】
  NTL3D_FillBitMapImage cg, cg.BasicColor.White '
  ReDim cg.Z_buffer(cg.Header.Nx - 1, cg.Header.Ny - 1) '【Zバッファの初期化】
  NTL3D_ClearZ_Buffer cg, NTL3D_Z_max '
End Sub

'###########################################################
'### 3D用画素バッファのカレントイメージをクリアする
'###########################################################
Public Sub NTL3D_FillBitMapImage(cg As NTL3D_BitMapRGB, P As NTL_ColorRGB)
  Dim i As Long, j As Long

  For j = 0 To cg.Header.Ny - 1
    For i = 0 To cg.Header.Nx - 1
      cg.PixelBuffer(i, j) = P
    Next i
  Next j
End Sub

'###########################################################
'### 3D用ZバッファのZ値をクリアする
'###########################################################
Public Sub NTL3D_ClearZ_Buffer(cg As NTL3D_BitMapRGB, z As Single)
  Dim i As Long, j As Long

  For j = 0 To cg.Header.Ny - 1
    For i = 0 To cg.Header.Nx - 1
      cg.Z_buffer(i, j) = z
    Next i
  Next j
End Sub

'###########################################################
'### 3D用カレントイメージをファイル化する
'###########################################################
Public Sub NTL3D_CreateBitMapFile(cg As NTL3D_BitMapRGB, filename As String)
  Open filename For Binary Access Write As #1
    Put #1, 1, cg.Header
    Put #1, 55, cg.PixelBuffer
  Close #1
End Sub

'###########################################################
'### 3D用カラーLookupテーブルを初期化する
'###########################################################
Public Sub NTL3D_InitializeColorLookupTable(cg As NTL3D_BitMapRGB, n As Long)
  cg.nColor = n
  ReDim cg.Color(n - 1)
End Sub

'###########################################################
'### 3D用カラーパレットの指定色間を補間した色を作る
'###########################################################
Public Sub NTL3D_CreateColor(cg As NTL3D_BitMapRGB, i1 As Long, P1 As NTL_ColorRGB, i2 As Long, P2 As NTL_ColorRGB)
  Dim Dr As Single, Dg As Single, Db As Single, i As Long

  Dr = (CSng(P2.R) - CSng(P1.R)) / CSng(i2 - i1)
  Dg = (CSng(P2.G) - CSng(P1.G)) / CSng(i2 - i1)
  Db = (CSng(P2.B) - CSng(P1.B)) / CSng(i2 - i1)
  For i = i1 To i2 Step Sgn(i2 - i1)
    cg.Color(i).R = P1.R + Dr * CSng(i - i1)
    cg.Color(i).G = P1.G + Dg * CSng(i - i1)
    cg.Color(i).B = P1.B + Db * CSng(i - i1)
  Next i
End Sub

'###########################################################
'### 3D用カラーパレットを作る(グレイ・スケール)
'###########################################################
Public Sub NTL3D_CreateGrayScale(cg As NTL3D_BitMapRGB, n As Long)
  NTL3D_InitializeColorLookupTable cg, n
  NTL3D_CreateColor cg, 0, cg.BasicColor.Black, n - 1, cg.BasicColor.White
End Sub

'###########################################################
'### 3D用カラーパレットを作る(正負)
'###########################################################
Public Sub NTL3D_CreateDipoleScale(cg As NTL3D_BitMapRGB, n As Long)
  Dim C0 As Long, C1 As Long, C2 As Long, C3 As Long, C4 As Long, C5 As Long

  C0 = n - 1
  C1 = n * 0.84
  C2 = n * 0.67
  C3 = n * 0.5
  C4 = n * 0.33
  C5 = n * 0.17
  NTL3D_InitializeColorLookupTable cg, n
  NTL3D_CreateColor cg, C0, cg.BasicColor.Red, C1, cg.BasicColor.Orange
  NTL3D_CreateColor cg, C1, cg.BasicColor.Orange, C2, cg.BasicColor.Yellow
  NTL3D_CreateColor cg, C2, cg.BasicColor.Yellow, C3, cg.BasicColor.Green
  NTL3D_CreateColor cg, C3, cg.BasicColor.DarkGreen, C4, cg.BasicColor.Cyan
  NTL3D_CreateColor cg, C4, cg.BasicColor.Cyan, C5, cg.BasicColor.Blue
  NTL3D_CreateColor cg, C5, cg.BasicColor.Blue, 0, cg.BasicColor.Violet
  cg.Color(C3) = cg.BasicColor.White
End Sub

'###########################################################
'### 3D用カラーパレットを作る(絶対値)
'###########################################################
Public Sub NTL3D_CreateMonopoleScale(cg As NTL3D_BitMapRGB, n As Long)
  Dim C0 As Long, C1 As Long, C2 As Long, C3 As Long, C4 As Long, C5 As Long, C6 As Long

  C0 = n - 1
  C1 = n * 10 / 12
  C2 = n * 8 / 12
  C3 = n * 6 / 12
  C4 = n * 5 / 12
  C5 = n * 4 / 12
  C6 = n * 2 / 12
  NTL3D_InitializeColorLookupTable cg, n
  ' 表示色数 赤⇒橙⇒黄⇒緑⇒淡青⇒青⇒濃灰⇒灰⇒淡灰⇒白
  NTL3D_CreateColor cg, C0, cg.BasicColor.Red, C1, cg.BasicColor.Orange
  NTL3D_CreateColor cg, C1, cg.BasicColor.Orange, C2, cg.BasicColor.Yellow
  NTL3D_CreateColor cg, C2, cg.BasicColor.Yellow, C3, cg.BasicColor.Green
  NTL3D_CreateColor cg, C3, cg.BasicColor.Green, C4, cg.BasicColor.DarkGreen
  NTL3D_CreateColor cg, C4, cg.BasicColor.DarkGreen, C5, cg.BasicColor.Blue
  NTL3D_CreateColor cg, C5, cg.BasicColor.Blue, C6, cg.BasicColor.Violet
  NTL3D_CreateColor cg, C6, cg.BasicColor.DarkGray, 0, cg.BasicColor.White
End Sub

'###########################################################
'###【MMD】3Dカレントイメージに指定色で点を描画する
'###########################################################
Public Sub MMD3D_DrawPixel(cg As NTL3D_BitMapRGB, pos As x_Vector, MMD_P As x_ColorRGBA)
  Dim P As NTL_ColorRGB

  P.R = (255 * MMD_P.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P.G = (255 * MMD_P.Green) And &HFF '
  P.B = (255 * MMD_P.Blue) And &HFF '
  NTL3D_DrawPixel cg, pos, P
End Sub

'###########################################################
'###【MMD】3Dカレントイメージに指定単色で直線を描画する(コンスタント・シェーディング)
'###########################################################
Public Sub MMD3D_DrawLineConstant(cg As NTL3D_BitMapRGB, pos1 As x_Vector, pos2 As x_Vector, MMD_P As x_ColorRGBA)
  Dim P As NTL_ColorRGB

  P.R = (255 * MMD_P.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P.G = (255 * MMD_P.Green) And &HFF '
  P.B = (255 * MMD_P.Blue) And &HFF '
  NTL3D_DrawLineShading cg, pos1, P, pos2, P
End Sub

'###########################################################
'###【MMD】 3Dカレントイメージに指定補間色で直線を描画する(カラー・シェーディング)
'###########################################################
Public Sub MMD3D_DrawLineShading(cg As NTL3D_BitMapRGB, pos1 As x_Vector, MMD_P1 As x_ColorRGBA, pos2 As x_Vector, MMD_P2 As x_ColorRGBA)
  Dim P1 As NTL_ColorRGB, P2 As NTL_ColorRGB

  P1.R = (255 * MMD_P1.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P1.G = (255 * MMD_P1.Green) And &HFF '
  P1.B = (255 * MMD_P1.Blue) And &HFF '
  P2.R = (255 * MMD_P2.Red) And &HFF '【色範囲変換】MMD,DirectXは範囲(0.0-1.0)、これを範囲(0-255)に変換する。
  P2.G = (255 * MMD_P2.Green) And &HFF '
  P2.B = (255 * MMD_P2.Blue) And &HFF '
  NTL3D_DrawLineShading cg, pos1, P1, pos2, P2
End Sub

'###########################################################
'###【NTL】3Dカレントイメージに指定色で点を描画する[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL3D_DrawPixel(cg As NTL3D_BitMapRGB, pos As x_Vector, P As NTL_ColorRGB) ' テクスチャーの折返し処理は上位コマンドで行う
  If (CLng(pos.x) >= 0 And CLng(pos.x) < cg.Header.Nx And CLng(pos.y) >= 0 And CLng(pos.y) < cg.Header.Ny) Then
    If pos.z <= cg.Z_buffer(CLng(pos.x), CLng(pos.y)) Then
      cg.PixelBuffer(CLng(pos.x), CLng(pos.y)) = P
      cg.Z_buffer(CLng(pos.x), CLng(pos.y)) = pos.z
    End If
  End If
End Sub

'###########################################################
'###【NTL】3Dカレントイメージに指定単色で直線を描画する(コンスタント・シェーディング)[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL3D_DrawLineConstant(cg As NTL3D_BitMapRGB, pos1 As x_Vector, pos2 As x_Vector, P As NTL_ColorRGB)
  NTL3D_DrawLineShading cg, pos1, P, pos2, P
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージに指定補間色で直線を描画する(カラー・シェーディング)[表示枠内座標のみ描画する]
'###########################################################
Public Sub NTL3D_DrawLineShading(cg As NTL3D_BitMapRGB, pos1 As x_Vector, P1 As NTL_ColorRGB, pos2 As x_Vector, P2 As NTL_ColorRGB)
  Dim Dx As Single, Dy As Single, Dz As Single, Dr As Single, Dg As Single, Db As Single, w As Single, MajorDir As Long
  Dim i As Long, j As Long

  If NTL3D_LineKerning(cg, pos1, pos2) Then GoTo LE ' この線分が表示枠に引っ掛からない場合、無処理。
  Dx = pos2.x - pos1.x
  Dy = pos2.y - pos1.y
  If Abs(Dx) > Abs(Dy) Then ' x座標がMajor軸(長軸)
    If Dx < 0 Then MajorDir = -1 Else MajorDir = 1 ' Major軸の補間進行方向を示す
    If CLng(Dx) <> 0 Then
      w = 1# / Dx
    Else
      w = 0
    End If
    Dy = Dy * w
    Dz = CSng(pos2.z - pos1.z) * w
    Dr = CSng(P2.R - P1.R) * w
    Dg = CSng(P2.G - P1.G) * w
    Db = CSng(P2.B - P1.B) * w
    For i = CLng(pos1.x) To CLng(pos2.x) Step MajorDir
      If (i >= 0 And i < cg.Header.Nx) Then
        j = pos1.y + Dy * CSng(i - CLng(pos1.x))
        If (j >= 0 And j < cg.Header.Ny) Then
          If cg.Z_buffer(i, j) >= (pos1.z + Dz * CSng(i)) Then
            cg.Z_buffer(i, j) = pos1.z + Dz * CSng(i)
            cg.PixelBuffer(i, j).R = P1.R + Dr * CSng(i)
            cg.PixelBuffer(i, j).G = P1.G + Dg * CSng(i)
            cg.PixelBuffer(i, j).B = P1.B + Db * CSng(i)
          End If
        End If
      End If
    Next i
  Else ' y座標がMajor軸(長軸)
    If Dy < 0 Then MajorDir = -1 Else MajorDir = 1 ' Major軸の補間進行方向を示す
    If CLng(Dy) <> 0 Then
      w = 1# / Dy
    Else
      w = 0
    End If
    Dx = Dx * w
    Dz = CSng(pos2.z - pos1.z) * w
    Dr = CSng(P2.R - P1.R) * w
    Dg = CSng(P2.G - P1.G) * w
    Db = CSng(P2.B - P1.B) * w
    For j = CLng(pos1.y) To CLng(pos2.y) Step MajorDir
      If (j >= 0 And j < cg.Header.Ny) Then
        i = pos1.x + Dx * CSng(j - CLng(pos1.y))
        If (i >= 0 And i < cg.Header.Nx) Then
          If cg.Z_buffer(i, j) >= (pos1.z + Dz * CSng(j)) Then
            cg.Z_buffer(i, j) = pos1.z + Dz * CSng(j)
            cg.PixelBuffer(i, j).R = P1.R + Dr * CSng(j)
            cg.PixelBuffer(i, j).G = P1.G + Dg * CSng(j)
            cg.PixelBuffer(i, j).B = P1.B + Db * CSng(j)
          End If
        End If
      End If
    Next j
  End If
LE:
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージに指定補間色で直線を描画する(カーニング) 枠に引っ掛かったときに線分をクリッピング処理していない。チェックのみ。
'###########################################################
Public Function NTL3D_LineKerning(cg As NTL3D_BitMapRGB, pos1 As x_Vector, pos2 As x_Vector) As Boolean
  Dim x As Single, y As Single, Dpos1 As Single, Dpos2 As Single, flag1 As Boolean, flag2 As Boolean

  NTL3D_LineClipping = False
  flag1 = (pos1.x >= 0) And (pos1.x < cg.Header.Nx) And (pos1.y >= 0) And (pos1.y < cg.Header.Ny) ' Trueなら頂点uv1は表示枠内
  flag2 = (pos2.x >= 0) And (pos2.x < cg.Header.Nx) And (pos2.y >= 0) And (pos2.y < cg.Header.Ny) ' Trueなら頂点uv2は表示枠内
  If (flag1 Or flag2) Then GoTo LE '【2頂点のいずれか一方が表示枠内】カーニング不可
  If (pos1.x = pos2.x) Or (pos1.y = pos2.y) Then GoTo LE
  Dpos1 = (pos2.x - pos1.x) / (pos2.y - pos1.y)
  Dpos2 = 1# / Dpos1
  '【交差チェック】y軸(y=0)及びy=cg.Header.Ny-1
  ' x = x1 + (y-y1)*(x2-x1)/(y2-y1)
  x = pos1.x - pos1.y * Dpos1
  If (x >= 0 And x < cg.Header.Nx) Then GoTo LE '【線分交差】枠下側 y=0
  x = pos1.x + (cg.Header.Ny - pos1.y) * Dpos1
  If (x >= 0 And x < cg.Header.Nx) Then GoTo LE '【線分交差】枠上側 y=cg.Header.Ny
  '【交差チェック】x軸(x=0)及びx=cg.Header.Nx-1
  ' y = y1 + (x-x1)(y2-y1)/(x2-x1)
  y = pos1.y - pos1.x * Dpos2
  If (y >= 0 And y < cg.Header.Ny) Then GoTo LE '【線分交差】枠左側 x=0
  y = pos1.y + (cg.Header.Nx - pos1.x) * Dpos2
  If (y >= 0 And y < cg.Header.Ny) Then GoTo LE '【線分交差】枠右側 x=cg.Header.Nx
  NTL3D_LineClipping = True '【2頂点とも表示枠外、かつ表示枠と交差しない⇒カーニング】
LE:
End Function

'###########################################################
'###【NTL】3Dカレント・イメージに指定補間色で三角形を描画する
'###########################################################
Public Sub NTL3D_Triangle(cg As NTL3D_BitMapRGB, pos1 As x_Vector, P1 As NTL_ColorRGB, pos2 As x_Vector, P2 As NTL_ColorRGB, pos3 As x_Vector, P3 As NTL_ColorRGB)
  Dim pos(2) As x_Vector, P(2) As NTL_ColorRGB
  pos(0) = pos1: pos(1) = pos2: pos(2) = pos3: P(0) = P1: P(1) = P2: P(2) = P3
  NTL3D_Polygon cg, 3, pos, P
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージに指定補間色で任意多角形を描画する
'###########################################################
Public Sub NTL3D_Polygon(cg As NTL3D_BitMapRGB, n As Long, pos() As x_Vector, pn() As NTL_ColorRGB)
  Dim Ymax As Single, Ymin As Single, Pmax As Long, Pmin As Long
  Dim posa1 As x_Vector, posa2 As x_Vector, posb1 As x_Vector, posb2 As x_Vector
  Dim pa1 As x_ColorRGBA, pa2 As x_ColorRGBA, pb1 As x_ColorRGBA, pb2 As x_ColorRGBA
  Dim Dxa As Single, Dza As Single, Dra As Single, Dga As Single, Dba As Single
  Dim Dxb As Single, Dzb As Single, Drb As Single, Dgb As Single, Dbb As Single
  Dim i As Long, Pa As Long, Pb As Long, Ca As Long, Cb As Long, wa As Single, wb As Single, w As Single
  Dim P() As x_ColorRGBA

  ReDim P(n - 1)
  Ymax = -NTL3D_Z_max: Ymin = NTL3D_Z_max
  For i = 0 To n - 1
    P(i).Red = pn(i).R
    P(i).Green = pn(i).G
    P(i).Blue = pn(i).B
    P(i).Alpha = 1
    If (pos(i).y > Ymax) Then
      Pmax = i: Ymax = pos(i).y
    End If
    If (pos(i).y < Ymin) Then
      Pmin = i: Ymin = pos(i).y
    End If
  Next i
  i = 0: Pa = Pmax: Pb = Pmax: Ca = 0: Cb = 0
GetALine:
  posa1 = pos(Pa): pa1 = P(Pa): Pa = Pa + 1: If Pa = n Then Pa = 0
  posa2 = pos(Pa): pa2 = P(Pa): wa = posa2.y - posa1.y: If Abs(wa) = 0 Then wa = 0 Else wa = 1# / wa
  Dxa = (posa2.x - posa1.x) * wa: Dza = (posa2.z - posa1.z) * wa: Dra = (pa2.Red - pa1.Red) * wa: Dga = (pa2.Green - pa1.Green) * wa: Dba = (pa2.Blue - pa1.Blue) * wa
  w = posa1.y - Int(posa1.y): posa1.y = Int(posa1.y): Ca = Abs(posa2.y - posa1.y) '【20090928修正】
  posa1.x = posa1.x - Dxa * w: posa1.z = posa1.z - Dza * w: pa1.Red = pa1.Red - Dra * w: pa1.Green = pa1.Green - Dga * w: pa1.Blue = pa1.Blue - Dba * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L3
GetBLine:
  posb1 = pos(Pb): pb1 = P(Pb): Pb = Pb - 1: If Pb = -1 Then Pb = n - 1
  posb2 = pos(Pb): pb2 = P(Pb): wb = posb2.y - posb1.y: If Abs(wb) = 0 Then wb = 0 Else wb = 1# / wb
  Dxb = (posb2.x - posb1.x) * wb: Dzb = (posb2.z - posb1.z) * wb: Drb = (pb2.Red - pb1.Red) * wb: Dgb = (pb2.Green - pb1.Green) * wb: Dbb = (pb2.Blue - pb1.Blue) * wb
  w = posb1.y - Int(posb1.y): posb1.y = Int(posb1.y): Cb = Abs(posb2.y - posb1.y) '【20090928修正】
  posb1.x = posb1.x - Dxb * w: posb1.z = posb1.z - Dzb * w: pb1.Red = pb1.Red - Drb * w: pb1.Green = pb1.Green - Dgb * w: pb1.Blue = pb1.Blue - Dbb * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L4
  i = 1
L1:
  If (Ca <= 0) And (Pa <> Pb) Then GoTo GetALine '【20090928修正】
  If (Cb <= 0) And (Pa <> Pb) Then GoTo GetBLine '【20090928修正】
  If (Ca <= 0) And (Cb <= 0) And (Pa = Pb) Then GoTo LE '【20090928修正】
L2:
  NTL3D_ScanLine cg, posa1, pa1, posb1, pb1
  ax = posa1.x: bx = posb1.x: ay = posa1.y: by = posb1.y '【Debug時座標確認用】
L3:
  If (Ca = 0) And (Pa <> Pb) Then '【20090928修正】
    i = 2: GoTo GetALine
  End If
  Ca = Ca - 1: posa1.y = posa1.y - 1: posa1.x = posa1.x - Dxa: posa1.z = posa1.z - Dza: pa1.Red = pa1.Red - Dra: pa1.Green = pa1.Green - Dga: pa1.Blue = pa1.Blue - Dba
L4:
  If (Cb = 0) And (Pa <> Pb) Then '【20090928修正】
    i = 2: GoTo GetBLine
  End If
  Cb = Cb - 1: posb1.y = posb1.y - 1: posb1.x = posb1.x - Dxb: posb1.z = posb1.z - Dzb: pb1.Red = pb1.Red - Drb: pb1.Green = pb1.Green - Dgb: pb1.Blue = pb1.Blue - Dbb
  GoTo L1
LE:
  NTL3D_ScanLine cg, posa1, pa1, posb1, pb1
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージに指定補間色でスキャンラインを描画する
'###########################################################
Public Sub NTL3D_ScanLine(cg As NTL3D_BitMapRGB, pos1 As x_Vector, P1 As x_ColorRGBA, pos2 As x_Vector, P2 As x_ColorRGBA)
  Dim poss As x_Vector, ps As x_ColorRGBA, pose As x_Vector, pe As x_ColorRGBA, NTL_P As NTL_ColorRGB
  Dim Dx As Single, Dz As Single, Dr As Single, Dg As Single, Db As Single, w As Single, u As Single, i As Long, j As Long

  If (pos1.x < pos2.x) Then '【スキャンラインを左から右へ描画するために始点と終点を決定する】
    poss = pos1: pose = pos2: ps = P1: pe = P2
  Else
    poss = pos2: pose = pos1: ps = P2: pe = P1
  End If
  If pose.x < 0 Then GoTo LE '【終点がx=0より小さいので終了】
  Dx = pose.x - poss.x: If Dx <> 0 Then w = 1# / Dx Else w = 0
  Dz = (pose.z - poss.z) * w
  Dr = (pe.Red - ps.Red) * w
  Dg = (pe.Green - ps.Green) * w
  Db = (pe.Blue - ps.Blue) * w
  If poss.x >= 0 Then '【20090928修正】
    u = poss.x - Int(poss.x) ' x軸方向微調整分(1画素未満)
    If u <> 0 Then u = 1 - u '
    If u > Dx Then GoTo LE ' 幅を超えているので終了
    poss.x = poss.x + u ' 整数座標に合わせる
  Else '
    u = -poss.x ' x軸までスキップする
    If u > Dx Then GoTo LE ' 幅を超えているので終了
    poss.x = 0 '
  End If '
  poss.z = poss.z + u * Dz
  ps.Red = ps.Red + u * Dr
  ps.Green = ps.Green + u * Dg
  ps.Blue = ps.Blue + u * Db
  j = CLng(pose.x - poss.x) '【20090928修正】
  For i = 0 To j
    NTL_P.R = CInt(ps.Red) And &HFF
    NTL_P.G = CInt(ps.Green) And &HFF
    NTL_P.B = CInt(ps.Blue) And &HFF
    NTL3D_DrawPixel cg, poss, NTL_P
    If i = j Then GoTo LE
    poss.x = poss.x + 1
    poss.z = poss.z + Dz
    ps.Red = ps.Red + Dr
    ps.Green = ps.Green + Dg
    ps.Blue = ps.Blue + Db
  Next i
LE:
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージにテクスチャー・マッピングで三角形を描画する
'###########################################################
Public Sub NTL3D_TriangleWithTextureMapping(cg As NTL3D_BitMapRGB, tx As NTL2D_BitMapRGB, pos1 As x_Vector, uv1 As x_Coords2d, pos2 As x_Vector, uv2 As x_Coords2d, pos3 As x_Vector, uv3 As x_Coords2d)
  Dim pos(2) As x_Vector, uv(2) As x_Coords2d
  pos(0) = pos1: pos(1) = pos2: pos(2) = pos3: uv(0) = uv1: uv(1) = uv2: uv(2) = uv3
  NTL3D_PolygonWithTextureMapping cg, tx, 3, pos, uv
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージにテクスチャー・マッピングで任意多角形を描画する
'###########################################################
Public Sub NTL3D_PolygonWithTextureMapping(cg As NTL3D_BitMapRGB, tx As NTL2D_BitMapRGB, n As Long, pos() As x_Vector, uv() As x_Coords2d)
  Dim Ymax As Single, Ymin As Single, Pmax As Long, Pmin As Long
  Dim posa1 As x_Vector, posa2 As x_Vector, posb1 As x_Vector, posb2 As x_Vector
  Dim uva1 As x_Coords2d, uva2 As x_Coords2d, uvb1 As x_Coords2d, uvb2 As x_Coords2d
  Dim Dxa As Single, Dza As Single, Dua As Single, Dva As Single
  Dim Dxb As Single, Dzb As Single, Dub As Single, Dvb As Single
  Dim i As Long, Pa As Long, Pb As Long, Ca As Long, Cb As Long, wa As Single, wb As Single, w As Single

  Ymax = -NTL3D_Z_max: Ymin = NTL3D_Z_max
  For i = 0 To n - 1
    If (pos(i).y > Ymax) Then
      Pmax = i: Ymax = pos(i).y
    End If
    If (pos(i).y < Ymin) Then
      Pmin = i: Ymin = pos(i).y
    End If
  Next i
  i = 0: Pa = Pmax: Pb = Pmax: Ca = 0: Cb = 0
GetALine:
  posa1 = pos(Pa): uva1 = uv(Pa): Pa = Pa + 1: If Pa = n Then Pa = 0
  posa2 = pos(Pa): uva2 = uv(Pa): wa = posa2.y - posa1.y: If Abs(wa) = 0 Then wa = 0 Else wa = 1# / wa
  Dxa = (posa2.x - posa1.x) * wa: Dza = (posa2.z - posa1.z) * wa: Dua = (uva2.u - uva1.u) * wa: Dva = (uva2.v - uva1.v) * wa
  w = posa1.y - Int(posa1.y): posa1.y = Int(posa1.y): Ca = Abs(posa2.y - posa1.y) '【20090928修正】
  posa1.x = posa1.x - Dxa * w: posa1.z = posa1.z - Dza * w: uva1.u = uva1.u - Dua * w: uva1.v = uva1.v - Dva * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L3
GetBLine:
  posb1 = pos(Pb): uvb1 = uv(Pb): Pb = Pb - 1: If Pb = -1 Then Pb = n - 1
  posb2 = pos(Pb): uvb2 = uv(Pb): wb = posb2.y - posb1.y: If Abs(wb) = 0 Then wb = 0 Else wb = 1# / wb
  Dxb = (posb2.x - posb1.x) * wb: Dzb = (posb2.z - posb1.z) * wb: Dub = (uvb2.u - uvb1.u) * wb: Dvb = (uvb2.v - uvb1.v) * wb
  w = posb1.y - Int(posb1.y): posb1.y = Int(posb1.y): Cb = Abs(posb2.y - posb1.y) '【20090928修正】
  posb1.x = posb1.x - Dxb * w: posb1.z = posb1.z - Dzb * w: uvb1.u = uvb1.u - Dub * w: uvb1.v = uvb1.v - Dvb * w
  If i = 1 Then GoTo L1
  If i = 2 Then GoTo L4
  i = 1
L1:
  If (Ca <= 0) And (Pa <> Pb) Then GoTo GetALine '【20090928修正】
  If (Cb <= 0) And (Pa <> Pb) Then GoTo GetBLine '【20090928修正】
  If (Ca <= 0) And (Cb <= 0) And (Pa = Pb) Then GoTo LE '【20090928修正】
L2:
  NTL3D_ScanLineWithTextureMapping cg, tx, posa1, uva1, posb1, uvb1
  ' ax = posa1.x: bx = posb1.x: ay = posa1.y: by = posb1.y '【Debug時座標確認用】
L3:
  If (Ca = 0) And (Pa <> Pb) Then '【20090928修正】
    i = 2: GoTo GetALine
  End If
  Ca = Ca - 1: posa1.y = posa1.y - 1: posa1.x = posa1.x - Dxa: posa1.z = posa1.z - Dza: uva1.u = uva1.u - Dua: uva1.v = uva1.v - Dva
L4:
  If (Cb = 0) And (Pa <> Pb) Then '【20090928修正】
    i = 2: GoTo GetBLine
  End If
  Cb = Cb - 1: posb1.y = posb1.y - 1: posb1.x = posb1.x - Dxb: posb1.z = posb1.z - Dzb: uvb1.u = uvb1.u - Dub: uvb1.v = uvb1.v - Dvb
  GoTo L1
LE:
  NTL3D_ScanLineWithTextureMapping cg, tx, posa1, uva1, posb1, uvb1
End Sub

'###########################################################
'###【NTL】3Dカレント・イメージにテクスチャー・マッピングでスキャンラインを描画する
'###########################################################
Public Sub NTL3D_ScanLineWithTextureMapping(cg As NTL3D_BitMapRGB, tx As NTL2D_BitMapRGB, pos1 As x_Vector, uv1 As x_Coords2d, pos2 As x_Vector, uv2 As x_Coords2d)
  Dim poss As x_Vector, uvs As x_Coords2d, pose As x_Vector, uve As x_Coords2d, NTL_P As NTL_ColorRGB
  Dim Dx As Single, Dz As Single, Du As Single, Dv As Single, w As Single, u As Single, i As Long, j As Long

  If (pos1.x < pos2.x) Then '【スキャンラインを左から右へ描画するために始点と終点を決定する】
    poss = pos1: pose = pos2: uvs = uv1: uve = uv2
  Else
    poss = pos2: pose = pos1: uvs = uv2: uve = uv1
  End If
  If pose.x < 0 Then GoTo LE '【終点がx=0より小さいので終了】
  Dx = pose.x - poss.x: If Dx <> 0 Then w = 1# / Dx Else w = 0
  Dz = (pose.z - poss.z) * w
  Du = (uve.u - uvs.u) * w
  Dv = (uve.v - uvs.v) * w
  If poss.x >= 0 Then '【20090928修正】
    u = poss.x - Int(poss.x) ' x軸方向微調整分(1画素未満)
    If u <> 0 Then u = 1 - u '
    If u > Dx Then GoTo LE ' 幅を超えているので終了
    poss.x = poss.x + u ' 整数座標に合わせる
  Else '
    u = -poss.x ' x軸までスキップする
    If u > Dx Then GoTo LE ' 幅を超えているので終了
    poss.x = 0 '
  End If '
  poss.z = poss.z + u * Dz
  uvs.u = uvs.u + u * Du
  uvs.v = uvs.v + u * Dv
  j = CLng(pose.x - poss.x) '【20090928修正】
  For i = 0 To j
    NTL_P = tx.PixelBuffer(CLng(uvs.u), CLng(uvs.v))
    NTL3D_DrawPixel cg, poss, NTL_P
    If i = j Then GoTo LE
    poss.x = poss.x + 1
    poss.z = poss.z + Dz
    uvs.u = uvs.u + Du
    uvs.v = uvs.v + Dv
  Next i
LE:
End Sub