トップへ(mam-mam.net/)

TViewport3D上に3Dポリゴンメッシュ(TMesh)を表示(FMX) ~Delphiソースコード集

検索:

TViewport3D上に3Dポリゴンメッシュ(TMesh)を表示(FMX) ~Delphiソースコード集

Delphiを起動して新規作成を行い、必要なコンポーネントをドラッグ&ドロップする

Delphi起動⇒ファイル⇒新規作成⇒マルチデバイスアプリケーションを選択し、「空のアプリケーション」を選択してOKボタンをクリックします。
TTimer、TViewPort3D、TColorMaterialSource、TLightMaterialSourceをフォームへドラッグ&ドロップします。
ViewPort3D1にTLightをドラッグ&ドロップし、Light1の方向を適当に左下へ向けます。
ViewPort3D1にTMeshをドラッグ&ドロップします。
Timer1のプロパティ[Interval]を33にします。

ソースコードを記述する

Form1のイベントonCreateイベントに以下のソースコードを記述し、 Form1のメンバにphase変数を追加、DrawWireプロシージャを追加してソースコードを記述します。
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Types3D,
  System.Math.Vectors, FMX.Objects3D, FMX.Controls3D, FMX.MaterialSources,
  FMX.Viewport3D, System.RTLConsts;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Viewport3D1: TViewport3D;
    ColorMaterialSource1: TColorMaterialSource;
    LightMaterialSource1: TLightMaterialSource;
    Light1: TLight;
    Mesh1: TMesh;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private 宣言 }
  public
    { public 宣言 }
    phase:single;  //位相
    procedure DrawWire(Sender: TObject; Context: TContext3D);
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.DrawWire(Sender: TObject; Context: TContext3D);
begin
  //線を描画する
  Context.DrawLines(
    TMesh(Sender).Data.VertexBuffer,
    TMesh(Sender).Data.IndexBuffer,
    ColorMaterialSource1.Material, 1
  );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  phase:=0;
  //マテリアル(材質)設定
  Mesh1.MaterialSource:=LightMaterialSource1;
  //両面にする場合
  //Mesh1.TwoSide:=true;

  //TMeshWrapMode.Fit
  //  TMeshのWidth,Height,Depthの中に収まるようにフィット
  //  比率は保たれ、原点は自動計算される
  //TMeshWrapMode.Original
  //  TMeshのWidth,Height,Depth倍された大きさになり、原点は0,0,0
  //TMeshWrapMode.Resize
  //  基本はFitと同じだが原点は0,0,0
  //TMeshWrapMode.Stretch
  //  TMeshのWidth,Height,Depthの中に収まるようにストレッチされ
  //  比率は保たれない、原点は自動計算される
  Mesh1.WrapMode:=TMeshWrapMode.Resize;
  Mesh1.Width:=4;
  Mesh1.Depth:=4;
  Mesh1.Height:=4;
  Mesh1.Position.X:=0;
  Mesh1.Position.Y:=-2;//少し上の位置にする
  Mesh1.Position.Z:=0;
  Mesh1.RotationAngle.X:=30;//30度回転
end;


Timer1ダブルクリックして以下のソースコードを記述します。
procedure TForm1.Timer1Timer(Sender: TObject);
var xp,zp:integer;
    x,z:integer;
    y:single;
    idx:integer;
begin
  xp:=20;  //x方向のメッシュの数+1
  zp:=15;  //z方向のメッシュの数+1

  Mesh1.Data.VertexBuffer.Length := xp*zp;
  Mesh1.Data.IndexBuffer.Length := (xp-1)*(zp-1)*3*2;

  //頂点バッファの作成
  for z := 0 to zp-1 do
  begin
    for x := 0 to xp-1 do
    begin
      //y座標(高さ)を適当にCos*Sinにする
      y:=Cos(x/xp*pi()*4+phase)*Sin(z/zp*pi()*4+phase)*2;
      //頂点バッファに値を設定
      Mesh1.Data.VertexBuffer.Vertices[x+z*xp]:=
        Point3D(x-xp/2, y, z-zp/2);
    end;
  end;

  //メッシュの作成
  idx:=0;
  for z := 0 to zp-2 do
  begin
    for x := 0 to xp-2 do
    begin
      //3角形ポリゴン作成 左回り
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+0)*xp;
      inc(idx);
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+1)*xp;
      inc(idx);
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+0)*xp;
      inc(idx);
      //3角形ポリゴン作成 左回り
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+0)*xp;
      inc(idx);
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+1)*xp;
      inc(idx);
      Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+1)*xp;
      inc(idx);
    end;
  end;

  //メッシュのすべての面に対する法線ベクトルを計算します
    Mesh1.Data.CalcFaceNormals();
    //メッシュのすべての面の滑らかな法線ベクトルを計算します
    //Mesh1.Data.CalcSmoothNormals();
    //メッシュのすべての面の接線ベクトルと従法線ベクトルを計算します
    //Mesh1.Data.CalcTangentBinormals;

  //稜線(ワイヤーフレーム)の描画が必要であれば以下の行のコメントを外す
  //Mesh1.OnRender:=DrawWire;

  //位相を変える
  phase:=phase+0.1;

  //再描画
  Viewport3D1.Repaint;
end;

実行する

実行ボタンを押して実行します。(デバッグ実行でもOKです。)