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了。