در درسهای قبلی در مورد load کردن اشیا از یک فایل X صحبت کردم اما در مورد چگونگی گرفتن اطلاعات vertex از یک شی Mesh صحبت نشد . کتابخانه D3DX برای اینکار دو تابع دارد : تابع D3DXMeshVertexBuffer8GetData : اطلاعات یک شی D3DXMesh را گرفته و در یک آرایه از D3DVERTEX ذخیره می کند : D3DXMeshVertexBuffer8GetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long D3DXMeshobj As Unknown = A D3DXMESH object that you want to extract the data from . Offset As Long = How far into the vertex buffer we want to start reading, 0 is the beginning Size As Long = Size of the vertex buffer, this will be Len(D3DVERTEX) * Mesh.GetNumVertices Flags As Long = A combination of the CONST_D3DLOCKFLAGS, leave as 0 . Data As Any = The first element in the array that you want the data to be read into, should be an array of D3DVERTEX vertices Return Code As Long = Returns D3D_OK for success, or either of D3DERR_INVALIDCALL or E_INVALIDARG for an error تابع D3DXMeshVertexBuffer8SetData : اطلاعات یک بافر vertex را در یک شی D3DXMesh قرار می دهد : D3DXMeshVertexBuffer8SetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long D3DXMeshobj As Unknown = The D3DXMESH object that defines where you want the data to be placed Offset As Long = How far into the Destination vertex buffer you want to place the data Size As Long = The Size of the buffer in bytes, this will be Len(D3DVERTEX) * Mesh.GetNumVertices Flags As Long = A Combination of the CONST_D3DLOCKFLAGS, leave as 0 Data As Any = The first element in the array of data you want placed in the mesh's vertex buffer Return Code As Long = D3D_OK for success or D3DERR_INVALIDCALL or E_INVALIDARG for failure عملیات انجام انیمیشن فریم کلیدی بصورت زیر است : load کردن اشیا از فایلهای X به درون شی D3DXMesh استخراج اطلاعات vertex از این شی انجام درون یابی بین فریمهای کلیدی قرار دادن اطلاعات vertex های درون یابی در یک شی D3DXMesh فرض می کنیم که انیمیشن ما همیشه از زمان صفر تا زمان n باشد – برحیب میلی ثانیه – بنابراین می توانیم از GetTickCount برای توابع زمانی خود استفاده کنیم . همچنین یک ساختار را برای هر فریم کلیدی بصورت زیر تعریف می کنیم : Private Type KeyFrame شی load شده از یک فایل’ Mesh As D3DXMesh آرایه material برای هر شی’ MatList() As D3DMATERIAL8 آرایه Texture’ TexList() As Direct3DTexture8 تعداد material ها و texture هایی که استفاده می کنیم’ nMaterials As Long داده های vertex برای این فریم کلیدی’ VertexList() As D3DVERTEX موقعیت این فریم کلیدی در انیمیشن’ TimeIndex As Long End Type حال باید تابعی بنویسیم که اطلاعات را از یک فایل X استخراج کرده و درون فریم کلیدی قرار دهد : Private Function CreateKeyFrameFromFile(Filename As String, TexturePrefix As String, Time As Long) As KeyFrame نام فایل X برای شی سه بعدی: Filename ’ پوشه ای که اطلاعات texture این شی در آن قرار دارد : TexturePrefix ’ اندیس زمان برای این فریم کلیدی : Time ' Dim I As Long Dim XBuffer As D3DXBuffer Dim TextureFile As String Dim hResult As Long 'خواندن اطلاعات از فایل ورودی به حافظه Set CreateKeyFrameFromFile.Mesh = D3DX.LoadMeshFromX(Filename, D3DXMESH_MANAGED, D3DDevice, Nothing, XBuffer, CreateKeyFrameFromFile.nMaterials)x تولید material ها و texture ها ‘ ReDim CreateKeyFrameFromFile.MatList(CreateKeyFrameFromFile.nMaterials) As D3DMATERIAL8 ReDim CreateKeyFrameFromFile.TexList(CreateKeyFrameFromFile.nMaterials) As Direct3DTexture8 For I = 0 To CreateKeyFrameFromFile.nMaterials - 1 D3DX.BufferGetMaterial XBuffer, I, CreateKeyFrameFromFile.MatList(I)x CreateKeyFrameFromFile.MatList(I).Ambient = CreateKeyFrameFromFile.MatList (I).diffuse TextureFile = D3DX.BufferGetTextureName(XBuffer, I)x If TextureFile < > "" Then Set CreateKeyFrameFromFile.TexList(I) = D3DX.CreateTextureFromFileEx(D3DDevice, TexturePrefix & TextureFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED , D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, 0, ByVal 0, ByVal 0)x End If Next I استخراج داده های vertex ’ ReDim CreateKeyFrameFromFile.VertexList(CreateKeyFrameFromFile.Mesh.GetNumVertices) As D3DVERTEX hResult = D3DXMeshVertexBuffer8GetData(CreateKeyFrameFromFile.Mesh, 0, Len(CreateKeyFrameFromFile.VertexList(0)) * reateKeyFrameFromFile.Mesh.GetNumVertices, 0, CreateKeyFrameFromFile.VertexList(0 (( CreateKeyFrameFromFile.TimeIndex = Time End Function در تابع Initialize خطوط زیر را برای ساخت فریم های کلیدی اضافه می کنیم : nKeyFrames = 4 kfAnimLength = 2500 AnimLastStartAt = GetTickCount()x ReDim kfAnim(nKeyFrames - 1) As KeyFrame kfAnim(0) = CreateKeyFrameFromFile(App.Path & "frame0.x", App.Path & "", 0)x kfAnim(1) = CreateKeyFrameFromFile(App.Path & "frame1.x", App.Path & "", kfAnimLength * (1 / 3))x kfAnim(2) = CreateKeyFrameFromFile(App.Path & "frame2.x", App.Path & "", kfAnimLength * (2 / 3))x kfAnim(3) = CreateKeyFrameFromFile(App.Path & "frame3.x", App.Path & "", kfAnimLength)x kfCurrent = CreateKeyFrameFromFile(App.Path & "frame0.x", App.Path & "", 0 ( دقت کنید که از یک اندیس زمان برای ساخت فریم های کلیدی استفاده شده است . حال باید کدی برای نمایش دادن انیمیشن بنویسیم . ابتدا باید به روشی تغییرات فریمها را کنترل کنیم : For I = 0 To nKeyFrames - 2 If CurrentTimeIndex >= kfAnim(I).TimeIndex Then PrevFrame = I NextFrame = I + 1 End If Next I سپس باید با توجه به زمان index دو فریم کلیدی و زمان جاری ، پارامتر درون یابی را محاسبه کنیم : sTime = kfAnim(PrevFrame).TimeIndex eTime = kfAnim(NextFrame).TimeIndex cTime = CurrentTimeIndex eTime = eTime - sTime cTime = cTime - sTime sTime = sTime - sTime InterpolateAmount = cTime / eTime سپس باید بر اساس این پارامتر عمل درون یابی را روی داده های vertex انجام دهیم : For I = 0 To kfCurrent.Mesh.GetNumVertices 'درون یابی مختصاتها D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).X, kfAnim(PrevFrame).VertexList(I).Y , _ kfAnim(PrevFrame).VertexList(I).Z), MakeVector(kfAnim(NextFrame).VertexList(I).X, kfAnim(NextFrame).VertexList(I).Y , _ kfAnim(NextFrame).VertexList(I).Z), InterpolateAmount kfCurrent.VertexList(I).X = vTemp3D.X kfCurrent.VertexList(I).Y = vTemp3D.Y kfCurrent.VertexList(I).Z = vTemp3D.Z 'درون یابی نرمالها D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).nx, kfAnim(PrevFrame).VertexList(I).ny , _ kfAnim(PrevFrame).VertexList(I).nz), MakeVector(kfAnim(NextFrame).VertexList(I).nx, kfAnim(NextFrame).VertexList(I).ny , _ kfAnim(NextFrame).VertexList(I).nz), InterpolateAmount kfCurrent.VertexList(I).nx = vTemp3D.X kfCurrent.VertexList(I).ny = vTemp3D.Y kfCurrent.VertexList(I).nz = vTemp3D.Z 'درون یابی اطلاعات بافت D3DXVec2Lerp vTemp2D, MakeVector2D(kfAnim(PrevFrame).VertexList(I).tu, kfAnim(PrevFrame).VertexList(I).tv ), _ MakeVector2D(kfAnim(NextFrame).VertexList(I).tu, kfAnim(NextFrame).VertexList(I).tv), InterpolateAmount kfCurrent.VertexList(I).tu = vTemp2D.X kfCurrent.VertexList(I).tv = vTemp2D.Y Next I حال باید داده تولید شده را به فرمت Mesh برگردانیم : hResult = D3DXMeshVertexBuffer8SetData(kfCurrent.Mesh, 0, Len(kfCurrent.VertexList(0)) * kfCurrent.Mesh.GetNumVertices, 0, kfCurrent.VertexList(0))x با استفاده از روش فوق می توانید هر تعداد فریم کلیدی را به انیمیشنتان اضافه کنید . اشکالی که روش فوق دارد اینست که اطلاعات texture برای تمام فریمهای کلیدی جداگانه ذخیره شده است در حالیکه texture در تمام فریمها ثابت است . در درسهای بعدی از روشی بنام texture pooling استفاده می کنیم تا تنها یک کپی از texture ها نگهداری کنیم .