熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

C#+Direct3D9.0開發實例之月亮繞著地球轉

2022-06-13   來源: .NET編程 
建立空窗體

  新建一個工程添加引用並導入名稱空間

  加入一個設備對象變量

private MicrosoftDirectXDirectDDevice device = null;
  添加初始化圖形函數並在這裡面對設備對象進行實例化

public void InitializeGraphics()
{
 PresentParameters presentParams = new PresentParameters();
 presentParamsWindowed = true;
 presentParamsSwapEffect = SwapEffectFlip;
 presentParamsAutoDepthStencilFormat = DepthFormatD;
 presentParamsEnableAutoDepthStencil = true;
 device = new MicrosoftDirectXDirectDDevice( MicrosoftDirectXDirectDDeviceTypeHardware this  CreateFlagsHardwareVertexProcessing presentParams);
}
  當程序執行時需要繪制場景代碼在這個函數裡

public void Render()
{
 // 清空設備並准備顯示下一幀
 deviceClear(ClearFlagsTarget | ClearFlagsZBuffer ColorBlack f );
 // 設置照相機的位置
 SetupCamera();
 //開始場景
 deviceBeginScene();
 if(meshLoaded)
 {
  meshRender(meshLoc);
 }
 deviceEndScene();
 //顯示設備內容
 devicePresent();
}
  設置照相機的位置

private void SetupCamera()
{
 deviceTransformProjection = MatrixPerspectiveFovLH((float)MathPI / thisWidth / thisHeight f f);
 deviceTransformView = MatrixLookAtLH(new Vector(f f f) new Vector(ff f) new Vector());
}
  現在改變主函數調用我們寫的初始化函數並顯示場景

[STAThread]

static void Main()
{
 using (Form EarthForm = new Form())
 {
  EarthFormInitializeGraphics();
  EarthFormShow();

  while(EarthFormCreated)
  {
   EarthFormRender();
   ApplicationDoEvents();
  }
  EarthFormDispose();
}
  運行程序會顯示一個空的窗體

加入地球

  在這一步裡需創建一個D網格對象來作為要顯示的地球為此在工程中新加入一個類Earth此類可以包含所創建的網格對象的信息

  加入一些相關變量含義見注釋

public class Earth : BaseEarth
{
 private Material[] mMaterials; //保存材質
 private Texture[] mTextures; //保存紋理
 private Matrix locationOffset; //用來保存網格對象的相對位置
 private Mesh mMesh = null; //三角形網格對象
 private Device meshDevice; //需要顯示在哪個設備上
}
  在構造函數中把Device設備拷貝到私有成員變量這樣就可以在這個類的其它方法內使用它另外就是把位置變量進行賦值

public Earth(ref Device device Matrix location): base(ref device)
{
 meshDevice = device;
 locationOffset = location;
}
  下面這個函數是裝入X文件

public bool LoadMesh(string meshfile)
{
 ExtendedMaterial[] mtrl;
 try
 {
  // 裝載文件
  mMesh = MeshFromFile(meshfile MeshFlagsManaged meshDevice out mtrl);
  // 如果有材質的話裝入它們
  if ((mtrl != null) && (mtrlLength > ))
  {
   mMaterials = new Material[mtrlLength];
   mTextures = new Texture[mtrlLength];

   // 得到材質和紋理

   for (int i = ; i < mtrlLength; i++)
   {
    mMaterials[i] = mtrl[i]MaterialD;
    if ((mtrl[i]TextureFilename != null) && (mtrl[i]TextureFilename != stringEmpty))
    {
     //前面得到的紋理的路徑是相對路徑需要保存的是絕對路徑通過應用程序路徑可以獲得
     mTextures[i] = TextureLoaderFromFile(meshDevice @\\ + mtrl[i]TextureFilename);
    }
   }
  }
  return true;
 }
 catch
 {
  return false;
 }
}
  在這個方法內使用MeshFromFile()這個方法從給定的文件名中找到X文件並裝入相關數據一旦數據格式設置完成可以從此文件中找到材質和貼圖信息並把它存放在數組中並通過文件路徑得到紋理文件文件的路徑最後返回真值如果整個過程出現錯誤返回假值

  下面這個Render()方法是把此對象即地球顯示在設備對象上此方法較簡單通過變形操作來得到網格對象的XYZ坐標接著設置網格對象的材質和紋理最後將每個材質和紋理應用到每個網格

public void Render(Matrix worldTransform)
{
 /把位置變為世界坐標
 meshDeviceTransformWorld = MatrixMultiply(locationOffset worldTransform);
 //繪制網格
 for (int i = ; i < mMaterialsLength; i++)
 {
  meshDeviceMaterial = mMaterials[i];
  meshDeviceSetTexture( mTextures[i]);
  mMeshDrawSubset(i);
 }
}
  現在回到窗體代碼中添加引用網格對象的相關變量

private Earth mesh = null;
private Matrix meshLoc;
private bool meshLoaded = false;
  在圖形初始化函數中需要對網格對象進行初始化加入下面的代碼

meshLoc = MatrixIdentity;
meshLocM = f;
mesh = new Earth(ref device meshLoc);
if (meshLoadMesh(@\\earthx))
{
 meshLoaded = true;
}
  代碼第一句把網格對象的位置定為原點接著偏移X軸個單位接下來從文件中得到此X文件如果成功設置meshLoaded置為真注意這裡有一個X文件在源代碼中有此文件

  在設置相機的函數中加入一盞燈光

deviceLights[]Type = LightTypeDirectional;
deviceLights[]Diffuse = ColorWhite;
deviceLights[]Direction = new Vector( );
deviceLights[]Update();
deviceLights[]Enabled = true;

  此燈光較簡單僅為一個直射型白光燈

最後在Render()方法中調用網格對象的Render()方法以顯示地球

  

  使地球旋轉

  前面用一個網格對象來建立地球但此類沒有平移旋轉及縮放等方法下面就加入這些方法因為這些方法具有通用性因此可以新建一個類把這些方法寫在這些類中使地球對象成為它的派生類

  在工程中新添加一個類BaseEarth

  加入進行平移旋轉縮放的變量

   private float xloc = f;
private float yloc = f;
private float zloc = f;
private float xrot = f;
private float yrot = f;
private float zrot = f;
private float xscale = f;
private float yscale = f;
private float zscale = f;

  加入相應的屬性代碼

   public float XLoc
{
 get
 {
  return xloc;
 }
 set
 {
  xloc = value;
 }
}
…………

  在Render()虛函數中應用平移旋轉及縮放

   public virtual void Render()
{
 objdeviceMultiplyTransform(TransformTypeWorldMatrixTranslation(xloc yloc zloc));
 objdeviceMultiplyTransform(TransformTypeWorldMatrixRotationAxis(new Vector(f f f) xrot));
 objdeviceMultiplyTransform(TransformTypeWorldMatrixRotationAxis(new Vector(f f f) yrot));
 objdeviceMultiplyTransform(TransformTypeWorldMatrixRotationAxis(new Vector(f f f) zrot));
 objdeviceMultiplyTransform(TransformTypeWorldMatrixScaling(xscale yscale zscale));
 return;
}

  現在回到地球類需要將其改為新類的派生類同時更改構造函數另外在Render()方法中應先調用基類的Render()方法

   public override void Render()
{
 baseRender();
 //把位置變為世界坐標
 // meshDeviceTransformWorld = MatrixMultiply(locationOffset worldTransform);
 //繪制網格
 
}

  現在由於在基類中可以設置對象位置因此可以把與locationOffset相關即與設置位置的變量及語句注釋掉

加入月球

  在這一步加入月球實際上是再創建一個網格對象新實例只是把紋理進行更改即可為了代碼模塊性更好把兩個對象放在一個新類CModel中在工程中新添加一個類CModel並聲明對象實例

   public class cModel
{
 private cMeshObject mesh = null;
 private cMeshObject mesh = null;
 private bool modelloaded;
}

  把窗口代碼中的Load()事件放在CModel中這次不僅生成了地球而且生成了月球

   public void Load(ref Device device)
{
 mesh = new Earth(ref device);
 mesh = new Earth(ref device);
 if (meshLoadMesh(@\\earthx))
 {
  modelloaded = true;
 }
 else
 {
  modelloaded = false;
 }
 if (meshLoadMesh(@\\moonx))
 {
  meshXLoc += f;
  modelloaded = true;
 }
 else
 {
  modelloaded = false;
 }
}

  下面的Update()方法中參數dir 用來判斷是順時針旋轉還是逆時針旋轉另外地球和月球繞Y軸增加的角度大小不同也就決定了二者旋轉的速度不同

   public void Update(int dir)
{
 if(dir > )
 {
  meshYRot += f;
  meshYRot += f;
 }
 else if(dir < )
 {
  meshYRot = f;
  meshYRot = f;
 }
}

  在下面的render()方法中生成顯示月球和地球

   public void Render(ref Device device)
{
 deviceTransformWorld = MatrixIdentity;
 if(modelloaded)
 {
  meshRender();
  meshRender();
 }
}

  把窗口代碼中的加入燈光的方法也放在此類中

   public void LoadLights(ref Device device)
{
 deviceLights[]Type = LightTypeDirectional;
 deviceLights[]Diffuse = ColorWhite;
 deviceLights[]Position = new Vector(f f f);
 deviceLights[]Direction = new Vector( );
}
public void Light(ref Device device)
{
 deviceLights[]Update();
 deviceLights[]Enabled = true;
}

  與鼠標交互操作

  為了實現與鍵盤鼠標交互新添加一個類CMouse添加引用MicrosoftDirectXDirectInput並添加命名空間加入相關變量

   private MicrosoftDirectXDirectInputDevice mouse = null;
public SystemThreadingAutoResetEvent MouseUpdated;
private float x y z = f;
private byte[] buttons;

  在下面的構造函數代碼中首先創建鼠標設備並初始化回調事件

   public CMouse(SystemWindowsFormsControl control)
{
 mouse = new MicrosoftDirectXDirectInputDevice(SystemGuidMouse);
 mouseSetCooperativeLevel(control CooperativeLevelFlagsBackground | CooperativeLevelFlagsNonExclusive);
 mousePropertiesAxisModeAbsolute = false;
 MouseUpdated = new SystemThreadingAutoResetEvent(false);
 mouseSetEventNotification(MouseUpdated);
 mouseAcquire();
 Update();
}

  下面的Update()方法中獲得鼠標的坐標值並賦給私有成員變量

   public void Update()
{
 MouseState state = mouseCurrentMouseState;
 x = stateX;
 y = stateY;
 z = stateZ;
 buttons = stateGetMouseButtons();
}

  還需要有一個函數來檢測鼠標左鍵是否按下

   public bool LeftButtonDown
{
 get
 {
  bool a;
  return a = (buttons[] != );
 }
}

  大結局

  現在已經做完了准備工作返回到窗口代碼中需要對這裡的代碼重新進行一些調整

  在圖形初始化函數中創建一個CModel類及CMouse類

   private CModel model = null;
private CMouse mouse = null;
private bool leftbuttondown = false;
private float mousexloc;

  添加對鼠標初始化的方法

   public void InitializeInput()
{
 mouse = new CMouse(this);
}

  添加UpdateInputState()方法當按下鼠標左鍵時將leftbuttondown值設置為真當鼠標抬起時將mousexloc置

   private void UpdateInputState()
{
 mouseUpdate();
 if (mouseLeftButtonDown)
 {
  if(leftbuttondown == false)
  {
   mousexloc = f;
   leftbuttondown = true;
  }
  else
  {
   mousexloc = mouseX;
  }
 }
 else
 {
  leftbuttondown = false;
  mousexloc = f;
 }
}

  在此程序中只對X值進行了操作即只能左右轉

  Render()方法更新如下

   public void Render()
{
 UpdateInputState();
 deviceClear(ClearFlagsTarget | ClearFlagsZBuffer ColorDarkGray f );
 SetupCamera();
 deviceBeginScene();
 modelUpdate((int)mousexloc);
 modelLight(ref device);
 modelRender(ref device);
 deviceEndScene();
 devicePresent();
}

  最後更改Main()主函數

   static void Main()
{
 using (Form EarthForm = new Form())
 {
  EarthFormInitializeGraphics();
  EarthFormInitializeInput();
  EarthFormShow();
  while(EarthFormCreated)
  {
   EarthFormRender();
   ApplicationDoEvents();
  }
  EarthFormDispose();
 }

  運行程序按下鼠標左鍵拖動即可旋轉月球與地球

  


From:http://tw.wingwit.com/Article/program/net/201311/13213.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.