//----------------------------------------------------------------------------- // File: SimpleAnimation.cs // // Simple skeletal animation using Managed DirectX // // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Microsoft.Samples.DirectX.UtilityToolkit; namespace SimpleAnimationSample { /**////<summary>SimpleAnimation Sample Class</summary> publicclass SimpleAnimation : IFrameworkCallback, IDeviceCreation { 无需关心之物0#region 无需关心之物0 Creation#region Creation /**////<summary>Create a new instance of the class</summary> public SimpleAnimation(Framework f) { // Store framework sampleFramework = f; // Create dialogs hud =new Dialog(sampleFramework); } #endregion // Variables private Framework sampleFramework =null; // Framework for samples private Font statsFont =null; // Font for drawing text private Sprite textSprite =null; // Sprite for batching text calls private ModelViewerCamera camera =new ModelViewerCamera(); // A model viewing camera privatebool isHelpShowing =false; // If true, renders the UI help text private Dialog hud =null; // dialog for standard controls // Sample specific items // Animation root frame private AnimationRootFrame rootFrame; privatefloat objectRadius =0.0f; // Radius of the object private Vector3 objectCenter; // Center of the object privatebool isDestroyed =false; privatestaticreadonly ColorValue WhiteColor =new ColorValue(1.0f, 1.0f, 1.0f, 1.0f); privatestaticreadonly ColorValue Color2 =new ColorValue(0.25f, 0.25f, 0.25f, 1.0f); // HUD Ui Control constants privateconstint ToggleFullscreen =1; privateconstint ToggleReference =3; privateconstint ChangeDevice =4; /**////<summary> /// Called during device initialization, this code checks the device for some /// minimum set of capabilities, and rejects those that don't pass by returning false. ///</summary> publicbool IsDeviceAcceptable(Caps caps, Format adapterFormat, Format backBufferFormat, bool windowed) { // Skip back buffer formats that don't support alpha blending if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType, adapterFormat, Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backBufferFormat)) returnfalse; returntrue; } /**////<summary> /// This callback function is called immediately before a device is created to allow the /// application to modify the device settings. The supplied settings parameter /// contains the settings that the framework has selected for the new device, and the /// application can make any desired changes directly to this structure. Note however that /// the sample framework will not correct invalid device settings so care must be taken /// to return valid device settings, otherwise creating the Device will fail. ///</summary> publicvoid ModifyDeviceSettings(DeviceSettings settings, Caps caps) { // If device doesn't support HW T&L or doesn't support 1.1 vertex shaders in HW // then switch to SWVP. if ( (!caps.DeviceCaps.SupportsHardwareTransformAndLight) || (caps.VertexShaderVersion <new Version(1,1)) || (caps.MaxVertexBlendMatrixIndex <12) ) { settings.BehaviorFlags = CreateFlags.SoftwareVertexProcessing; } else { settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing; } // This application is designed to work on a pure device by not using // any get methods, so create a pure device if supported and using HWVP. if ( (caps.DeviceCaps.SupportsPureDevice) && ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) !=0 ) ) settings.BehaviorFlags |= CreateFlags.PureDevice; // Debugging vertex shaders requires either REF or software vertex processing // and debugging pixel shaders requires REF. #if(DEBUG_VS) if (settings.DeviceType != DeviceType.Reference ) { settings.BehaviorFlags &=~CreateFlags.HardwareVertexProcessing; settings.BehaviorFlags |= CreateFlags.SoftwareVertexProcessing; } #endif #if(DEBUG_PS) settings.DeviceType = DeviceType.Reference; #endif // For the first device created if its a REF device, optionally display a warning dialog box if (settings.DeviceType == DeviceType.Reference) { Utility.DisplaySwitchingToRefWarning(sampleFramework, "Simple Animation"); } } #endregion /**////<summary> /// 这里面有载入动画数据的代码 ///</summary> privatevoid OnCreateDevice(object sender, DeviceEventArgs e) { // 设置字体 statsFont = ResourceCache.GetGlobalInstance().CreateFont(e.Device, 15, 0, FontWeight.Bold, 1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, PitchAndFamily.FamilyDoNotCare | PitchAndFamily.DefaultPitch , "Arial"); // 创建动画分配层 // 传入this是为了在创建新容器的时候调用this的GenerateSkinnedMesh方法 AnimationAllocation alloc =new AnimationAllocation(this); // 寻找tiny.x string meshFile = Utility.FindMediaFile("tiny\\tiny.x"); string currentFolder = System.IO.Directory.GetCurrentDirectory(); System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(meshFile).Directory.FullName); // 读取层次 // rootFrame是AnimationRootFrame rootFrame = Mesh.LoadHierarchyFromFile(meshFile, MeshFlags.Managed, e.Device, alloc, null); // 设置目录 System.IO.Directory.SetCurrentDirectory(currentFolder); // 计算碰撞盒 objectRadius = Frame.CalculateBoundingSphere(rootFrame.FrameHierarchy, out objectCenter); // 设置动画的矩阵 SetupBoneMatrices(rootFrame.FrameHierarchy as AnimationFrame); isDestroyed =false; } 无需关心之物1#region 无需关心之物1 /**////<summary> /// This event will be fired immediately after the Direct3D device has been /// reset, which will happen after a lost device scenario. This is the best location to /// create Pool.Default resources since these resources need to be reloaded whenever /// the device is lost. Resources created here should be released in the OnLostDevice /// event. ///</summary> privatevoid OnResetDevice(object sender, DeviceEventArgs e) { SurfaceDescription desc = e.BackBufferDescription; // Create a sprite to help batch calls when drawing many lines of text textSprite =new Sprite(e.Device); // Setup the camera's projection parameters float aspectRatio = (float)desc.Width / (float)desc.Height; camera.SetProjectionParameters((float)Math.PI /4, aspectRatio, objectRadius /64.0f, objectRadius *200.0f); camera.SetViewParameters(new Vector3(0, 0, -2.2f* objectRadius), Vector3.Empty); camera.SetWindow(desc.Width, desc.Height); // Set device transforms e.Device.Transform.Projection = camera.ProjectionMatrix; e.Device.Transform.View = camera.ViewMatrix; // Setup a light e.Device.Lights[0].Type = LightType.Directional; e.Device.Lights[0].Direction =new Vector3(0.0f, -1.0f, 1.0f); e.Device.Lights[0].DiffuseColor = WhiteColor; e.Device.Lights[0].Enabled =true; // Setup UI locations hud.SetLocation(desc.Width-170, 0); hud.SetSize(170,170); } /**////<summary> /// This event function will be called fired after the Direct3D device has /// entered a lost state and before Device.Reset() is called. Resources created /// in the OnResetDevice callback should be released here, which generally includes all /// Pool.Default resources. See the "Lost Devices" section of the documentation for /// information about lost devices. ///</summary> privatevoid OnLostDevice(object sender, EventArgs e) { if (textSprite !=null) { textSprite.Dispose(); textSprite =null; } } /**////<summary> /// This callback function will be called immediately after the Direct3D device has /// been destroyed, which generally happens as a result of application termination or /// windowed/full screen toggles. Resources created in the OnCreateDevice callback /// should be released here, which generally includes all Pool.Managed resources. ///</summary> privatevoid OnDestroyDevice(object sender, EventArgs e) { if (isDestroyed) return; // Only clean up once Frame.Destroy(rootFrame.FrameHierarchy, new AnimationAllocation(this)); if (rootFrame.AnimationController !=null) { rootFrame.AnimationController.Dispose(); } isDestroyed =true; } #endregion /**////<summary> /// 渲染之前的场景更新阶段 /// 用来更新动画状态 ///</summary> publicvoid OnFrameMove(Device device, double appTime, float elapsedTime) { // 更新摄像机位置 camera.FrameMove(elapsedTime); // 计算世界矩阵 Matrix worldMatrix = Matrix.Translation(-objectCenter); worldMatrix *= camera.WorldMatrix; // 设置世界矩阵 device.Transform.World = worldMatrix; // 时间流逝? if (elapsedTime >0.0f) { if (rootFrame.AnimationController !=null) //重要!!! //此处推移了动画时间 //如果用Conv3ds从3ds文件转换出X文件的话 //DX的动画时间与3DMAX的比例是1:10 //即,某一个动作在3DMAX中位于32帧,那在DX中就应该位于3.2帧。 rootFrame.AnimationController.AdvanceTime(elapsedTime); UpdateFrameMatrices(rootFrame.FrameHierarchy as AnimationFrame, worldMatrix); } } /**////<summary> /// 场景渲染阶段 ///</summary> publicvoid OnFrameRender(Device device, double appTime, float elapsedTime) { bool beginSceneCalled =false; // 清屏 device.Clear(ClearFlags.ZBuffer | ClearFlags.Target, 0x002D32AA, 1.0f, 0); try { device.BeginScene(); beginSceneCalled =true; // 渲染动画 DrawFrame(rootFrame.FrameHierarchy as AnimationFrame); // 渲染FPS RenderText(); // 渲染UI hud.OnRender(elapsedTime); } finally { if (beginSceneCalled) device.EndScene(); } } /**////<summary>更新框架矩阵</summary> privatevoid UpdateFrameMatrices(AnimationFrame frame, Matrix parentMatrix) { frame.CombinedTransformationMatrix = frame.TransformationMatrix * parentMatrix; if (frame.FrameSibling !=null) { UpdateFrameMatrices(frame.FrameSibling as AnimationFrame, parentMatrix); } if (frame.FrameFirstChild !=null) { UpdateFrameMatrices(frame.FrameFirstChild as AnimationFrame, frame.CombinedTransformationMatrix); } } /**////<summary>绘制框架</summary> privatevoid DrawFrame(AnimationFrame frame) { AnimationMeshContainer mesh = frame.MeshContainer as AnimationMeshContainer; while(mesh !=null) { DrawMeshContainer(mesh, frame); mesh = mesh.NextContainer as AnimationMeshContainer; } if (frame.FrameSibling !=null) { DrawFrame(frame.FrameSibling as AnimationFrame); } if (frame.FrameFirstChild !=null) { DrawFrame(frame.FrameFirstChild as AnimationFrame); } } /**////<summary>渲染网格容器</summary> privatevoid DrawMeshContainer(AnimationMeshContainer mesh, AnimationFrame parent) { Device device = sampleFramework.Device; // 检查是否有蒙皮网格 if (mesh.SkinInformation !=null) { if (mesh.NumberInfluences ==1) device.RenderState.VertexBlend = VertexBlend.ZeroWeights; else device.RenderState.VertexBlend = (VertexBlend)(mesh.NumberInfluences -1); if (mesh.NumberInfluences >0) device.RenderState.IndexedVertexBlendEnable =true; BoneCombination[] bones = mesh.GetBones(); //重要!!此处绘制网格 for(int iAttrib =0; iAttrib < mesh.NumberAttributes; iAttrib++) { // 首先,获取世界矩阵 for (int iPaletteEntry =0; iPaletteEntry < mesh.NumberPaletteEntries; ++iPaletteEntry) { int iMatrixIndex = bones[iAttrib].BoneId[iPaletteEntry]; if (iMatrixIndex !=-1) { device.Transform.SetWorldMatrixByIndex(iPaletteEntry, mesh.GetOffsetMatrices()[iMatrixIndex] * mesh.GetFrames()[iMatrixIndex]. CombinedTransformationMatrix); } } // 设置材质 device.Material = mesh.GetMaterials()[bones[iAttrib].AttributeId].Material3D; device.SetTexture(0, mesh.GetTextures()[bones[iAttrib].AttributeId]); // 最后,绘子集 mesh.MeshData.Mesh.DrawSubset(iAttrib); } } else { // 如果是普通的网格,简单渲染之 device.RenderState.VertexBlend = VertexBlend.Disable; // 设置变换矩阵 device.Transform.World = parent.CombinedTransformationMatrix; ExtendedMaterial[] materials = mesh.GetMaterials(); //重要!!此处绘制网格 for (int i =0; i < materials.Length; ++i) { device.Material = materials[i].Material3D; device.SetTexture(0, mesh.GetTextures()[i]); mesh.MeshData.Mesh.DrawSubset(i); } } } 无需关心之物2#region 无需关心之物2 /**////<summary> /// Render the help and statistics text. This function uses the Font object for /// efficient text rendering. ///</summary> privatevoid RenderText() { TextHelper txtHelper =new TextHelper(statsFont, textSprite, 15); // Output statistics txtHelper.Begin(); txtHelper.SetInsertionPoint(5,5); txtHelper.SetForegroundColor(System.Drawing.Color.Yellow); txtHelper.DrawTextLine(sampleFramework.FrameStats); txtHelper.DrawTextLine(sampleFramework.DeviceStats); txtHelper.SetForegroundColor(System.Drawing.Color.White); txtHelper.DrawTextLine("Rendering simple animation"); // Draw help if (isHelpShowing) { txtHelper.SetInsertionPoint(10, sampleFramework.BackBufferSurfaceDescription.Height-15*6); txtHelper.SetForegroundColor(System.Drawing.Color.DarkOrange); txtHelper.DrawTextLine("Controls (F1 to hide):"); txtHelper.SetInsertionPoint(40, sampleFramework.BackBufferSurfaceDescription.Height-15*5); txtHelper.DrawTextLine("Quit: Esc"); txtHelper.DrawTextLine("Hide help: F1"); } else { txtHelper.SetInsertionPoint(10, sampleFramework.BackBufferSurfaceDescription.Height-15*2); txtHelper.SetForegroundColor(System.Drawing.Color.White); txtHelper.DrawTextLine("Press F1 for help"); } txtHelper.End(); } /**////<summary> /// As a convenience, the sample framework inspects the incoming windows messages for /// keystroke messages and decodes the message parameters to pass relevant keyboard /// messages to the application. The framework does not remove the underlying keystroke /// messages, which are still passed to the application's MsgProc callback. ///</summary> privatevoid OnKeyEvent(object sender, System.Windows.Forms.KeyEventArgs e) { switch(e.KeyCode) { case System.Windows.Forms.Keys.F1: isHelpShowing =!isHelpShowing; break; } } /**////<summary> /// Before handling window messages, the sample framework passes incoming windows /// messages to the application through this callback function. If the application sets /// noFurtherProcessing to true, the sample framework will not process the message ///</summary> public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, refbool noFurtherProcessing) { // Give the dialog a chance to handle the message first noFurtherProcessing = hud.MessageProc(hWnd, msg, wParam, lParam); if (noFurtherProcessing) return IntPtr.Zero; // Pass all remaining windows messages to camera so it can respond to user input camera.HandleMessages(hWnd, msg, wParam, lParam); return IntPtr.Zero; } #endregion /**////<summary> /// 产生蒙皮网格数据 ///</summary> publicvoid GenerateSkinnedMesh(AnimationMeshContainer mesh) { if (mesh.SkinInformation ==null) thrownew ArgumentException(); MeshFlags flags = MeshFlags.OptimizeVertexCache; Caps caps = sampleFramework.DeviceCaps; if (caps.VertexShaderVersion >=new Version(1,1)) { flags |= MeshFlags.Managed; } else { flags |= MeshFlags.SystemMemory; } int numMaxFaceInfl; using(IndexBuffer ib = mesh.MeshData.Mesh.IndexBuffer) { numMaxFaceInfl = mesh.SkinInformation.GetMaxFaceInfluences(ib, mesh.MeshData.Mesh.NumberFaces); } // 12个调色板入口能保证所有三角形都被处理 numMaxFaceInfl = (int)Math.Min(numMaxFaceInfl, 12); if (caps.MaxVertexBlendMatrixIndex +1>= numMaxFaceInfl) { mesh.NumberPaletteEntries = (int)Math.Min((caps. MaxVertexBlendMatrixIndex+1) /2, mesh.SkinInformation.NumberBones); flags |= MeshFlags.Managed; } int influences =0; BoneCombination[] bones =null; // 用ConvertToBlendedMesh来创建一个可以绘制的网格 MeshData data = mesh.MeshData; data.Mesh = mesh.SkinInformation.ConvertToIndexedBlendedMesh(data.Mesh, flags, mesh.GetAdjacencyStream(), mesh.NumberPaletteEntries, out influences, out bones); // 把数据保存下来 mesh.NumberInfluences = influences; mesh.SetBones(bones); // 获取属性数 mesh.NumberAttributes = bones.Length; mesh.MeshData = data; } /**////<summary>这个方法为框架设置骨骼</summary> privatevoid SetupBoneMatrices(AnimationFrame frame) { // 如果有父级 if (frame.MeshContainer !=null) { SetupBoneMatrices(frame.MeshContainer as AnimationMeshContainer); } // 如果有同级 if (frame.FrameSibling !=null) { SetupBoneMatrices(frame.FrameSibling as AnimationFrame); } // 如果还有子集 if (frame.FrameFirstChild !=null) { SetupBoneMatrices(frame.FrameFirstChild as AnimationFrame); } } /**////<summary>为容器设置骨骼</summary> privatevoid SetupBoneMatrices(AnimationMeshContainer mesh) { // 如果有蒙皮信息 if (mesh.SkinInformation !=null) { int numberBones = mesh.SkinInformation.NumberBones; AnimationFrame[] frameMatrices =new AnimationFrame[numberBones]; for(int i =0; i< numberBones; i++) { AnimationFrame frame = Frame.Find(rootFrame.FrameHierarchy, mesh.SkinInformation.GetBoneName(i)) as AnimationFrame; if (frame ==null) thrownew InvalidOperationException("Could not find valid bone."); frameMatrices[i] = frame; } mesh.SetFrames(frameMatrices); } } 无需关心之物3#region 无需关心之物3 /**////<summary> /// Initializes the application ///</summary> publicvoid InitializeApplication() { int y =10; // Initialize the HUD Button fullScreen = hud.AddButton(ToggleFullscreen,"Toggle full screen", 35, y, 125,22); Button toggleRef = hud.AddButton(ToggleReference,"Toggle reference (F3)", 35, y +=24, 125,22); Button changeDevice = hud.AddButton(ChangeDevice,"Change Device (F2)", 35, y +=24, 125,22); // Hook the button events for when these items are clicked fullScreen.Click +=new EventHandler(OnFullscreenClicked); toggleRef.Click +=new EventHandler(OnRefClicked); changeDevice.Click +=new EventHandler(OnChangeDevicClicked); } /**////<summary>Called when the change device button is clicked</summary> privatevoid OnChangeDevicClicked(object sender, EventArgs e) { sampleFramework.ShowSettingsDialog(!sampleFramework.IsD3DSettingsDialogShowing); } /**////<summary>Called when the full screen button is clicked</summary> privatevoid OnFullscreenClicked(object sender, EventArgs e) { sampleFramework.ToggleFullscreen(); } /**////<summary>Called when the ref button is clicked</summary> privatevoid OnRefClicked(object sender, EventArgs e) { sampleFramework.ToggleReference(); } /**////<summary> /// Entry point to the program. Initializes everything and goes into a message processing /// loop. Idle time is used to render the scene. ///</summary> staticint Main() { System.Windows.Forms.Application.EnableVisualStyles(); using(Framework sampleFramework =new Framework()) { SimpleAnimation sample =new SimpleAnimation(sampleFramework); // Set the callback functions. These functions allow the sample framework to notify // the application about device changes, user input, and windows messages. The // callbacks are optional so you need only set callbacks for events you're interested // in. However, if you don't handle the device reset/lost callbacks then the sample // framework won't be able to reset your device since the application must first // release all device resources before resetting. Likewise, if you don't handle the // device created/destroyed callbacks then the sample framework won't be able to // recreate your device resources. sampleFramework.Disposing +=new EventHandler(sample.OnDestroyDevice); sampleFramework.DeviceLost +=new EventHandler(sample.OnLostDevice); sampleFramework.DeviceCreated +=new DeviceEventHandler(sample.OnCreateDevice); sampleFramework.DeviceReset +=new DeviceEventHandler(sample.OnResetDevice); sampleFramework.SetWndProcCallback(new WndProcCallback(sample.OnMsgProc)); sampleFramework.SetCallbackInterface(sample); try { // Show the cursor and clip it when in full screen sampleFramework.SetCursorSettings(true, true); // Initialize sample.InitializeApplication(); // Initialize the sample framework and create the desired window and Direct3D // device for the application. Calling each of these functions is optional, but they // allow you to set several options which control the behavior of the sampleFramework. sampleFramework.Initialize( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes sampleFramework.CreateWindow("SimpleAnimation"); // Hook the keyboard event sampleFramework.Window.KeyDown +=new System.Windows.Forms.KeyEventHandler(sample.OnKeyEvent); sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, sample); // Pass control to the sample framework for handling the message pump and // dispatching render calls. The sample framework will call your FrameMove // and FrameRender callback when there is idle time between handling window messages. sampleFramework.MainLoop(); } #if(DEBUG) catch (Exception e) { // In debug mode show this error (maybe - depending on settings) sampleFramework.DisplayErrorMessage(e); #else catch { // In release mode fail silently #endif // Ignore any exceptions here, they would have been handled by other areas return (sampleFramework.ExitCode ==0) ?1 : sampleFramework.ExitCode; // Return an error code here } // Perform any application-level cleanup here. Direct3D device resources are released within the // appropriate callback functions and therefore don't require any cleanup code here. return sampleFramework.ExitCode; } } #endregion } 从 Frame 派生#region 从 Frame 派生 /**////<summary> /// 包含网格动画信息 ///</summary> publicclass AnimationFrame : Frame { // Store the combined transformation matrix private Matrix combined = Matrix.Identity; /**////<summary>组合变换</summary> public Matrix CombinedTransformationMatrix { get{ return combined; }set{ combined = value; } } } #endregion 从 MeshContainer 派生#region 从 MeshContainer 派生 /**////<summary> /// 包含动画信息 ///</summary> publicclass AnimationMeshContainer : MeshContainer { // 数组 private Texture[] meshTextures =null; private BoneCombination[] bones; private Matrix[] offsetMatrices; private AnimationFrame[] frameMatrices; // 实例数据 privateint numAttributes =0; privateint numInfluences =0; privateint numPalette =0; 公开属性#region 公开属性 /**////<summary>获取纹理</summary> public Texture[] GetTextures() { return meshTextures; } /**////<summary>设置纹理</summary> publicvoid SetTextures(Texture[] textures) { meshTextures = textures; } /**////<summary>获取骨骼组合</summary> public BoneCombination[] GetBones() { return bones; } /**////<summary>设置骨骼组合</summary> publicvoid SetBones(BoneCombination[] b) { bones = b; } /**////<summary>获取帧</summary> public AnimationFrame[] GetFrames() { return frameMatrices; } /**////<summary>设置帧</summary> publicvoid SetFrames(AnimationFrame[] frames) { frameMatrices = frames; } /**////<summary>获取偏移矩阵</summary> public Matrix[] GetOffsetMatrices() { return offsetMatrices; } /**////<summary>设置偏移矩阵</summary> publicvoid SetOffsetMatrices(Matrix[] matrices) { offsetMatrices = matrices; } /**////<summary>获取或设置属性数</summary> publicint NumberAttributes { get{ return numAttributes; }set{ numAttributes = value; } } /**////<summary>获取或设置影响数</summary> publicint NumberInfluences { get{ return numInfluences; }set{ numInfluences = value; } } /**////<summary>获取调色板入口数</summary> publicint NumberPaletteEntries { get{ return numPalette; }set{ numPalette = value; } } #endregion } #endregion 动画分配层#region 动画分配层 /**////<summary> /// 从 AllocateHierarchy 派生 ///</summary> publicclass AnimationAllocation : AllocateHierarchy { SimpleAnimation parent =null; /**////<summary>创建新实例</summary> public AnimationAllocation(SimpleAnimation p) { parent = p; } /**////<summary>创建帧</summary> publicoverride Frame CreateFrame(string name) { AnimationFrame frame =new AnimationFrame(); frame.Name = name; frame.TransformationMatrix = Matrix.Identity; frame.CombinedTransformationMatrix = Matrix.Identity; return frame; } /**////<summary>创建新容器</summary> publicoverride MeshContainer CreateMeshContainer(string name, MeshData meshData, ExtendedMaterial[] materials, EffectInstance[] effectInstances, GraphicsStream adjacency, SkinInformation skinInfo) { if (meshData.Mesh ==null) thrownew ArgumentException(); // 顶点必须要有格式 if (meshData.Mesh.VertexFormat == VertexFormats.None) thrownew ArgumentException(); AnimationMeshContainer mesh =new AnimationMeshContainer(); mesh.Name = name; int numFaces = meshData.Mesh.NumberFaces; Device dev = meshData.Mesh.Device; // 必须要有法线 if ((meshData.Mesh.VertexFormat & VertexFormats.Normal) ==0) { // 复制网格 Mesh tempMesh = meshData.Mesh.Clone(meshData.Mesh.Options.Value, meshData.Mesh.VertexFormat | VertexFormats.Normal, dev); // 丢弃旧的,用新的。新的有法线。 meshData.Mesh.Dispose(); meshData.Mesh = tempMesh; meshData.Mesh.ComputeNormals(); } // 保存材质 mesh.SetMaterials(materials); mesh.SetAdjacency(adjacency); Texture[] meshTextures =new Texture[materials.Length]; // 读纹理 for (int i =0; i < materials.Length; i++) { if (materials[i].TextureFilename !=null) { meshTextures[i] = ResourceCache.GetGlobalInstance().CreateTextureFromFile( dev, materials[i].TextureFilename); } } mesh.SetTextures(meshTextures); mesh.MeshData = meshData; // 如果有蒙皮,就把需要的信息保存下来 if (skinInfo !=null) { mesh.SkinInformation = skinInfo; int numBones = skinInfo.NumberBones; Matrix[] offsetMatrices =new Matrix[numBones]; for (int i =0; i < numBones; i++) offsetMatrices[i] = skinInfo.GetBoneOffsetMatrix(i); mesh.SetOffsetMatrices(offsetMatrices); parent.GenerateSkinnedMesh(mesh); } return mesh; } } #endregion }