unit VRPath;

{ TODO :
路径处理
包括Bezier }

interface

uses
  DXCommon,VRCommon,VRModelObject,Direct3D9,D3DX9,Windows,VRMaterial;

const
  PathFVF= D3DFVF_XYZ or D3DFVF_DIFFUSE;
  Framecy =1.0; //点的采样频率

Type
  TPathVertex = packed record
    Position : TPoint3;
    Color : TPoint3;
  end;

  PPathVertex = ^TPathVertex;

  TVRBezierPath = class(TInterfacedObject,IVRBezierPath)
  private
    Model : IDirect3DVertexBuffer9;
    Material : IVRMaterial;
    p : array[0..3] of TPoint3;
    FPS : single;//动画的长度
    StartTime : Single;
    Time : single;
    Enable : Boolean;
    VertexNum : integer;
    OnStart,OnStop : TVRPathMethod;
    function CreateModel() : Boolean;
    Procedure UnInit();
    function Calc(const p1,p2,p3,p4 : TPoint3; const Detla : single ) : TPoint3;overload;
    function CalcCountFromLen( const p1,p2 : TPoint3 ) : integer;//根据两点的距离,根据频率计算 点的个数
  public
    Procedure Start();
    Procedure Stop();
    function Init(const Len : single; const CanModel : Boolean ) : Boolean;overload;//是否有曲线模型
    function Init( const P1,p2,p3,p4 :TPoint3 ; const Len : single=1000;const CanModel : Boolean = false) : Boolean;overload;
    function Calc() : TPoint3;overload;//计算获得值
    function ReCalc() : TPoint3;
     function GetEnable() : Boolean;
     Procedure SetOnStart( const method : TVRPathMethod );
     Procedure SetOnStop( const method : TVRPathMethod );
     Procedure RenderModel();
  end;

implementation

{ TVRBezierPath }

function TVRBezierPath.Calc(const p1,p2,p3,p4 : TPoint3; const Detla : single): TPoint3; //detla范围在0~1 之间
var
  out1,out2,out3,out4 : TPoint3;
begin
{
 out = start * (1.0f - Detla) * (1.0f - Detla) * (1.0f - Detla) +
         cnt1 * 3.0f * Detla * (1.0f - Detla) * (1.0f - Detla) +
         cnt2 * 3.0f * Detla * Detla * (1.0f - Detla) +
         end * Detla * Detla * Detla;
}
 
  out1 := Point3MulSingle( p1,(1-Detla) * (1.0 - Detla) * (1.0 - Detla));
  out2 := Point3MulSingle( p2,3.0*detla*(1.0-detla)*(1.0-detla));
  out3 := Point3MulSingle( p3,3.0*detla*detla*(1.0-detla));
  out4 := Point3MulSingle( p4,Detla*Detla*Detla );

  Result := Point3Add( out1,out2 );
  Result := Point3Add( Result,out3 );
  Result := Point3Add( Result,out4 );
end;

function TVRBezierPath.Calc: TPoint3;
var
  Detla : single;
begin
  if Enable = false then exit;
  if FPS = 0 then exit;
  Time := Windows.GetTickCount;
  Detla:=(Time - StartTime)/FPS;
  if Detla > 1.0 then
  begin
    Result := Calc( p[0],p[1],p[2],p[3],1.0 );//还是要把最后一针渲染出来
    Stop;
  end
  else
    Result := Calc( p[0],p[1],p[2],p[3],Detla );
end;

function TVRBezierPath.CreateModel: Boolean;
var
 n,n1,n2,n3 : integer;
 Buf : Pchar;
 i : integer;
 Detla : single;
begin
  Result := false;
  //根据距离计算
  n1:=CalcCountFromLen( p[0],p[1] );
  n2:=CalcCountFromLen( p[1],p[2] );
  n3:= CalcCountFromLen( P[3],P[2] );
  n := n1 + n2 + n3;    //总共的顶点数量
  VertexNum := n;
  if n = 0 then exit;
  if Failed( DXDevice.CreateVertexBuffer(n*Sizeof(TPathVertex),0,PathFVF,D3DPOOL_DEFAULT,Model,nil) ) then exit;
  if Failed( Model.Lock(0,0,Pointer(Buf),0) ) then exit;
  for i:=0 to n - 1 do
  begin
    Detla := i/n;
    PPathVertex( Buf + i*Sizeof(TPathVertex) )^.Position := Calc(p[0],p[1],p[2],p[3],Detla);
    PPathVertex( Buf + i*Sizeof(TPathVertex) )^.Color := Point3( 255,255,255 );
  end;
  Model.Unlock;

  Material := TVRMaterial.Create;
 
  Result := True;
end;

function TVRBezierPath.Init(const Len : single;const CanModel: Boolean): Boolean;
begin
  UnInit;
  Result := false;
  FPS := Len;
  Time := 0.0;
  VertexNum := 0;
  if CanModel then
  begin
     if CreateModel = false then exit;
  end;
  Result := True;
end;


function TVRBezierPath.GetEnable: Boolean;
begin
  Result := Enable;
end;

function TVRBezierPath.Init(const P1, p2, p3, p4: TPoint3;
  const Len: single; const CanModel: Boolean): Boolean;
begin
  Result := false;
  p[0]:=p1;
  p[1]:=p2;
  p[2]:=p3;
  p[3]:=p4;
  if Init( Len,CanModel ) = false then exit;
  Result := True;
end;

 

procedure TVRBezierPath.Start;
begin
  StartTime := Windows.GetTickCount;
  if Assigned( OnStart ) then
    OnStart();
  Enable := true;
end;

procedure TVRBezierPath.Stop;
begin
  Enable := false;
  if Assigned( OnStop ) then
    OnStop(); 
end;

procedure TVRBezierPath.UnInit;
begin
  OnStart := nil;
  OnStop := nil;
  VertexNum := 0;
  Self.Model := nil;
  Material := nil;
end;

function TVRBezierPath.ReCalc: TPoint3;
var
  Detla : single;
begin
  if Enable = false then exit;
  if FPS = 0 then exit;
  Time := Windows.GetTickCount;
  Detla:=(Time - StartTime)/FPS;
  if Detla > 1.0 then
  begin
    Result := Calc( p[3],p[2],p[1], p[0],1.0 );
    Stop;
  end
  else
   Result := Calc( p[3],p[2],p[1], p[0],Detla );
end;

procedure TVRBezierPath.SetOnStart(const method: TVRPathMethod);
begin
  OnStart := Method;
end;

procedure TVRBezierPath.SetOnStop(const method: TVRPathMethod);
begin
 OnStop := Method;
end;

procedure TVRBezierPath.RenderModel;
begin
  if Material <> nil then
    Material.PrePare;
  if Model = nil then exit;
  DXDevice.SetStreamSource(0,Model,0,sizeof(TPathVertex));
  DXDevice.SetFVF(PathFVF);
  DXDevice.DrawPrimitive(D3DPT_LINESTRIP,0,VertexNum - 1) 
end;

function TVRBezierPath.CalcCountFromLen(const p1, p2: TPoint3): integer;
var
  v : TD3DVector;
  Len : single;
begin
  D3DX9.D3DXVec3Subtract(v,Point3ToVector(p2),Point3ToVector(p1));
  Len := D3DX9.D3DXVec3Length(v);
  Result := Trunc( Len*Framecy );
end;

end.

插入个预览图,图上一个3D MD3模型,绕着Bezeir曲线做运动,图中的黑色曲线就是Bezeir了。