Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

DelphiのFMXフレームワークで3Dメッシュ(TMesh)を表示する


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

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


ソースコードを記述する

Form1のイベントonCreateイベントに以下のソースコードを記述し、 Form1のメンバにphase変数を追加、DrawWireプロシージャを追加してソースコードを記述します。
省略・・・・
type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Viewport3D1: TViewport3D;
    ColorMaterialSource1: TColorMaterialSource;
    LightMaterialSource1: TLightMaterialSource;
    Light1: TLight;
    Mesh1: TMesh;
    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;
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.MaterialSource:=LightMaterialSource1;
  //Mesh1.TwoSide:=true;

  Mesh1.Position.X:=0;
  Mesh1.Position.Y:=2;
  Mesh1.Position.Z:=0;

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

  //頂点バッファの作成
  for x := 0 to xp-1 do
  begin
    for z := 0 to zp-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 x := 0 to xp-2 do
  begin
    for z := 0 to zp-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.OnRender:=DrawWire;

  //位相を変える
  phase:=phase+0.1;
  //再描画
  Viewport3D1.Repaint;
end;

実行する

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

実行中画面




全ソースコード

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;

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;
end;

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.MaterialSource:=LightMaterialSource1;
  //Mesh1.TwoSide:=true;

  Mesh1.Position.X:=0;
  Mesh1.Position.Y:=1;
  Mesh1.Position.Z:=0;

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

  //頂点バッファの作成
  for x := 0 to xp-1 do
  begin
    for z := 0 to zp-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 x := 0 to xp-2 do
  begin
    for z := 0 to zp-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.OnRender:=DrawWire;

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

  Viewport3D1.Repaint;
end;

end.

Copyright 2019 Mam