c#

位置:IT落伍者 >> c# >> 浏览文章

C#+Direct3D9.0开发实例之月亮绕着地球转


发布日期:2023年07月25日
 
C#+Direct3D9.0开发实例之月亮绕着地球转
建立空窗体

新建一个工程添加引用并导入名称空间

加入一个设备对象变量

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();

}

运行程序按下鼠标左键拖动即可旋转月球与地球

               

上一篇:用C#获取计算机磁盘空间

下一篇:C#中事件处理的个人体会