上一次中途结束了本来应该讲到的控制mesh的细节程度的方法的,这一次补上。
我们这里使用的是简单的方法,并没有涉及到场景剔出等等复杂的方法,我这里主要还是用dx9提供给我们的类库,progressive meshe。
progressive meshes主要的优点就是允许我们控制顶点和面的数目,这样我们就可以灵活的控制mesh细节的显示程度。
和mesh一样,progressive meshe也都是继承自basemesh这个类。比较重要的属性主要有2个numberfaces和numbervertices。从字面我们就可以看出两个属性的含义。
有了前面的基础在使用progressive mesh上也变得十分的简单,首先我们需要声明一个progressive mesh的变量,然后我们看看progressive mesh的构造函数:
public progressivemesh(
mesh mesh,
graphicsstream adjacency,
graphicsstream vertexweights, //顶点的权值,越高越不容易被剔出,如果设为null就全部被设为1
int minvalue, //设定被简化的最小值
meshflags options
);
以下就是核心的代码:
private void loadmesh(string file)
{
extendedmaterial[] mtrl;
graphicsstream adj;
// load our mesh
using(mesh mesh = mesh.fromfile(file, meshflags.managed, device,
out adj, out mtrl))
{
// if we have any materials, store them
if ((mtrl != null) && (mtrl.length > 0))
{
meshmaterials = new material[mtrl.length];
meshtextures = new texture[mtrl.length];
// store each material and texture
for (int i = 0; i < mtrl.length; i++)
{
meshmaterials[i] = mtrl[i].material3d;
if ((mtrl[i].texturefilename != null) &&
(mtrl[i].texturefilename != string.empty))
{
// we have a texture, try to load it
meshtextures[i] = textureloader.fromfile(device,
@”..\..\” + mtrl[i].texturefilename);
}
}
}
// clean our main mesh
using(mesh tempmesh = mesh.clean(mesh, adj, adj))
{
// create our progressive mesh
progressivemesh = new progressivemesh(tempmesh, adj,
null, 1, meshflags.simplifyvertex);
// set the initial mesh to the max
progressivemesh.numberfaces = progressivemesh.maxfaces;
progressivemesh.numbervertices = progressivemesh.maxvertices;
}
}
}
其实这里大部分代码和前面的关于mesh那一节的代码差不多,关于progressivemesh,微软的sdk也有一个例子,不过那个例子太过于庞大,大部分都是在处理ui方面,真正涉及到progressivemesh的部分还是很少的。
以下是代码的事例图和完整代码:
这个例子里可以切换线框渲染模式和实体作色模式。
using system;
using system.drawing;
using system.collections;
using system.componentmodel;
using system.windows.forms;
using system.data;
using microsoft.directx;
using microsoft.directx.direct3d;
namespace progressivemesh
{
/// <summary>
/// summary description for form1.
/// </summary>
public class form1 : system.windows.forms.form
{
private device device = null;
private progressivemesh progressivemesh = null;
private material[] meshmaterials;
private texture[] meshtextures;
private microsoft.directx.direct3d.font font = null;
private float camerapos = 8.0f;
private const int moveamount = 2;
/// <summary>
/// required designer variable.
/// </summary>
private system.componentmodel.container components = null;
private float angle = 0.0f;
public form1()
{
//
// required for windows form designer support
//
initializecomponent();
this.setstyle(controlstyles.allpaintinginwmpaint | controlstyles.opaque, true);
}
/// <summary>
/// we will initialize our graphics device here
/// </summary>
public void initializegraphics()
{
// set our presentation parameters
presentparameters presentparams = new presentparameters();
presentparams.windowed = true;
presentparams.swapeffect = swapeffect.discard;
presentparams.autodepthstencilformat = depthformat.d16;
presentparams.enableautodepthstencil = true;
// create our device
device = new device(0, devicetype.hardware, this, createflags.softwarevertexprocessing, presentparams);
// load our mesh
loadmesh(@”..\.. printracer.x”);
// create our font
font = new microsoft.directx.direct3d.font(device, new system.drawing.font
(“arial”, 14.0f, fontstyle.bold | fontstyle.italic));
}
private void loadmesh(string file)
{
extendedmaterial[] mtrl;
graphicsstream adj;
// load our mesh
using(mesh mesh = mesh.fromfile(file, meshflags.managed, device,
out adj, out mtrl))
{
// if we have any materials, store them
if ((mtrl != null) && (mtrl.length > 0))
{
meshmaterials = new material[mtrl.length];
meshtextures = new texture[mtrl.length];
// store each material and texture
for (int i = 0; i < mtrl.length; i++)
{
meshmaterials[i] = mtrl[i].material3d;
if ((mtrl[i].texturefilename != null) &&
(mtrl[i].texturefilename != string.empty))
{
// we have a texture, try to load it
meshtextures[i] = textureloader.fromfile(device,
@”..\..\” + mtrl[i].texturefilename);
}
}
}
// clean our main mesh
using(mesh tempmesh = mesh.clean(cleantype.simplification ,mesh, adj, adj))
{
// create our progressive mesh
progressivemesh = new progressivemesh(tempmesh, adj,
null, 1, meshflags.simplifyvertex);
// set the initial mesh to the max
progressivemesh.numberfaces = progressivemesh.maxfaces;
progressivemesh.numbervertices = progressivemesh.maxvertices;
}
}
}
private void setupcamera()
{
device.transform.projection = matrix.perspectivefovlh((float)math.pi / 4, this.width / this.height, 1.0f, 10000.0f);
device.transform.view = matrix.lookatlh(new vector3(0,0, camerapos),
new vector3(), new vector3(0,1,0));
device.renderstate.ambient = color.blue;
device.lights[0].type = lighttype.directional;
device.lights[0].diffuse = color.white;
device.lights[0].direction = new vector3(0, -1, -1);
device.lights[0].update();
device.lights[0].enabled = true;
}
protected override void onpaint(system.windows.forms.painteventargs e)
{
device.clear(clearflags.target | clearflags.zbuffer, color.blue , 1.0f, 0);
setupcamera();
device.beginscene();
// draw our mesh
drawmesh(angle / (float)math.pi, angle / (float)math.pi * 2.0f, angle / (float)math.pi / 4.0f, 0.0f, 0.0f, 0.0f);
font.drawtext(null, string.format(“number vertices in mesh: {0}”,
((basemesh)progressivemesh).numbervertices), new rectangle(10, 10, 0, 0),
drawtextformat.noclip, color.blanchedalmond);
font.drawtext(null, string.format(“number faces in mesh: {0}”,
((basemesh)progressivemesh).numberfaces), new rectangle(10, 30, 0, 0),
drawtextformat.noclip, color.blanchedalmond);
device.endscene();
device.present();
this.invalidate();
}
private void drawmesh(float yaw, float pitch, float roll, float x, float y, float z)
{
angle += 0.11f;
device.transform.world = matrix.rotationyawpitchroll(yaw, pitch, roll) * matrix.translation(x, y, z);
for (int i = 0; i < meshmaterials.length; i++)
{
device.material = meshmaterials[i];
device.settexture(0, meshtextures[i]);
progressivemesh.drawsubset(i);
}
}
protected override void onkeypress(keypresseventargs e)
{
if (e.keychar == +)
{
camerapos += (moveamount * 2);
progressivemesh.numbervertices =
((basemesh)progressivemesh).numbervertices – moveamount;
progressivemesh.numberfaces =
((basemesh)progressivemesh).numberfaces – moveamount;
}
if (e.keychar == -)
{
camerapos -= (moveamount * 2);
progressivemesh.numbervertices =
((basemesh)progressivemesh).numbervertices + moveamount;
progressivemesh.numberfaces =
((basemesh)progressivemesh).numberfaces + moveamount;
}
if (e.keychar == w)
device.renderstate.fillmode = fillmode.wireframe;
if (e.keychar == s)
device.renderstate.fillmode = fillmode.solid;
}
/// <summary>
/// clean up any resources being used.
/// </summary>
protected override void dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.dispose();
}
}
base.dispose( disposing );
}
#region windows form designer generated code
/// <summary>
/// required method for designer support – do not modify
/// the contents of this method with the code editor.
/// </summary>
private void initializecomponent()
{
this.components = new system.componentmodel.container();
this.size = new size(800,600);
this.text = “form1”;
}
#endregion
/// <summary>
/// the main entry point for the application.
/// </summary>
static void
main()
{
using (form1 frm = new form1())
{
// show our form and initialize our graphics engine
frm.show();
frm.initializegraphics();
application.run(frm);
}
}
}
}
这里我们已经实现了能自由控制显示的细节问题,通过progressivemesh 我们可以很方便的完成这一点,接下来,顺着这个话题我们来讨论一下如何增加细节的显示程度,也就是说我们将要把读入的mesh的细节程度增多。这里我们部探讨原理,因为我数学也是很差,我们可以借助patch meshes 来实现。
patch meshes通过增加读入的mesh的顶点来对细节程度进行增加。
public patchmesh(id3dxpatchmesh); 这就是pathmesh的构造函数,只要传入一个读入的mesh就可以了。接下来我们将用一个例子展示怎么增加显示的细节程度
private void createpatchmesh(string file, float tesslevel)
{
if (tesslevel < 1.0f) //如果小于默认的值,不再增加,直接返回
return;
if (mesh != null)
mesh.dispose();
using (mesh tempmesh = loadmesh(file))
{
using (patchmesh patch = patchmesh.createnpatchmesh(tempmesh))
{
// calculate the new number of faces/vertices
int numberfaces = (int)(tempmesh.numberfaces
* math.pow(tesslevel, 3));
int numberverts = (int)(tempmesh.numbervertices
* math.pow(tesslevel, 3));
mesh = new mesh(numberfaces, numberverts, meshflags.managed
| meshflags.use32bit, tempmesh.vertexformat, device);
// tessellate the patched mesh
patch.tessellate(tesslevel, mesh); //在tesslevel的基础上把mesh分成小方格
}
}
}
private mesh loadmesh(string file)
{
……………………….略
if ((mesh.vertexformat & vertexformats.normal) != vertexformats.normal)
//如果没有法线信息,就要计算法线,发现信息会在上面的tessellate方法中被用到。
{
// we must have normals for our patch meshes
mesh tempmesh = mesh.clone(mesh.options.value,
mesh.vertexformat | vertexformats.normal, device);
tempmesh.computenormals();
mesh.dispose();
mesh = tempmesh;
}
return mesh;
}
以下就是运行的图例
可以明显地看出,我们的程序还是有明显的效果的,不过对于增加的细节,程序将变得十分的缓慢。
以下是全部的代码:
using system;
using system.drawing;
using system.collections;
using system.componentmodel;
using system.windows.forms;
using system.data;
using microsoft.directx;
using microsoft.directx.direct3d;
namespace pathmesh
{
/// <summary>
/// summary description for form1.
/// </summary>
public class form1 : system.windows.forms.form
{
private device device = null;
private mesh mesh = null;
private material[] meshmaterials;
private texture[] meshtextures;
private float tesslevel = 1.0f;
private const float tessincrement = 1.0f;
private string filename = @”..\.. phere.x”;
private microsoft.directx.direct3d.font font = null;
/// <summary>
/// required designer variable.
/// </summary>
private system.componentmodel.container components = null;
private float angle = 0.0f;
public form1()
{
//
// required for windows form designer support
//
initializecomponent();
this.setstyle(controlstyles.allpaintinginwmpaint | controlstyles.opaque, true);
}
/// <summary>
/// we will initialize our graphics device here
/// </summary>
public void initializegraphics()
{
// set our presentation parameters
presentparameters presentparams = new presentparameters();
presentparams.windowed = true;
presentparams.swapeffect = swapeffect.discard;
presentparams.autodepthstencilformat = depthformat.d16;
presentparams.enableautodepthstencil = true;
// create our device
device = new device(0, devicetype.hardware, this, createflags.softwarevertexprocessing, presentparams);
// create our patch mesh
createpatchmesh(filename, tesslevel);
// create our font
font = new microsoft.directx.direct3d.font(device, new system.drawing.font
(“arial”, 14.0f, fontstyle.bold | fontstyle.italic));
// default to wireframe mode first
device.renderstate.fillmode = fillmode.wireframe;
}
/// <summary>
/// creates a temporary mesh object, and a patch mesh from it
/// </summary>
/// <param name=”file”>the original mesh to use</param>
/// <param name=”tesslevel”>the tesselation level</param>
private void createpatchmesh(string file, float tesslevel)
{
if (tesslevel < 1.0f) // nothing to do
return;
if (mesh != null)
mesh.dispose();
using (mesh tempmesh = loadmesh(file))
{
using (patchmesh patch = patchmesh.createnpatchmesh(tempmesh))
{
// calculate the new number of faces/vertices
int numberfaces = (int)(tempmesh.numberfaces
* math.pow(tesslevel, 3));
int numberverts = (int)(tempmesh.numbervertices
* math.pow(tesslevel, 3));
mesh = new mesh(numberfaces, numberverts, meshflags.managed
| meshflags.use32bit, tempmesh.vertexformat, device);
// tessellate the patched mesh
patch.tessellate(tesslevel, mesh);
}
}
}
/// <summary>
/// load a mesh from a file and return it
/// </summary>
/// <param name=”file”>the file to load</param>
/// <returns>the created mesh</returns>
private mesh loadmesh(string file)
{
extendedmaterial[] mtrl;
// load our mesh
mesh mesh = mesh.fromfile(file, meshflags.managed, device,
out mtrl);
// if we have any materials, store them
if ((mtrl != null) && (mtrl.length > 0))
{
meshmaterials = new material[mtrl.length];
meshtextures = new texture[mtrl.length];
// store each material and texture
for (int i = 0; i < mtrl.length; i++)
{
meshmaterials[i] = mtrl[i].material3d;
if ((mtrl[i].texturefilename != null) &&
(mtrl[i].texturefilename != string.empty))
{
// we have a texture, try to load it
meshtextures[i] = textureloader.fromfile(device,
@”..\..\” + mtrl[i].texturefilename);
}
}
}
if ((mesh.vertexformat & vertexformats.normal) != vertexformats.normal)
{
// we must have normals for our patch meshes
mesh tempmesh = mesh.clone(mesh.options.value,
mesh.vertexformat | vertexformats.normal, device);
tempmesh.computenormals();
mesh.dispose();
mesh = tempmesh;
}
return mesh;
}
private void setupcamera()
{
device.transform.projection = matrix.perspectivefovlh(
(float)math.pi / 4, this.width / this.height, 1.0f, 100.0f);
device.transform.view = matrix.lookatlh(new vector3(0,0, 5.0f),
new vector3(), new vector3(0,1,0));
device.lights[0].type = lighttype.directional;
device.lights[0].diffuse = color.darkkhaki;
device.lights[0].direction = new vector3(0, 0, -1);
device.lights[0].update();
device.lights[0].enabled = true;
}
protected override void onpaint(system.windows.forms.painteventargs e)
{
device.clear(clearflags.target | clearflags.zbuffer, color.cornflowerblue, 1.0f, 0);
setupcamera();
device.beginscene();
// draw our mesh
drawmesh(angle / (float)math.pi, angle / (float)math.pi * 2.0f, angle / (float)math.pi / 4.0f, 0.0f, 0.0f, 0.0f);
font.drawtext(null, string.format
(“number vertices: {0}\r\nnumber faces: {1}”,
mesh.numbervertices, mesh.numberfaces),
new rectangle(10,10,0,0),
drawtextformat.noclip, color.black);
device.endscene();
device.present();
this.invalidate();
}
private void drawmesh(float yaw, float pitch, float roll, float x, float y, float z)
{
angle += 0.01f;
device.transform.world = matrix.rotationyawpitchroll(yaw, pitch, roll) * matrix.translation(x, y, z);
for (int i = 0; i < meshmaterials.length; i++)
{
device.material = meshmaterials[i];
device.settexture(0, meshtextures[i]);
mesh.drawsubset(i);
}
}
protected override void onkeypress(keypresseventargs e)
{
if (e.keychar == +)
{
tesslevel += tessincrement;
createpatchmesh(filename, tesslevel);
}
if (e.keychar == -)
{
tesslevel -= tessincrement;
createpatchmesh(filename, tesslevel);
}
if (e.keychar == c)
{
filename = @”..\..\cube.x”;
tesslevel = 1.0f;
createpatchmesh(filename, tesslevel);
}
if (e.keychar == o)
{
filename = @”..\.. phere.x”;
tesslevel = 1.0f;
createpatchmesh(filename, tesslevel);
}
if (e.keychar == t)
{
filename = @”..\..\tiger.x”;
tesslevel = 1.0f;
createpatchmesh(filename, tesslevel);
}
if (e.keychar == w)
device.renderstate.fillmode = fillmode.wireframe;
if (e.keychar == s)
device.renderstate.fillmode = fillmode.solid;
}
/// <summary>
/// clean up any resources being used.
/// </summary>
protected override void dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.dispose();
}
}
base.dispose( disposing );
}
#region windows form designer generated code
/// <summary>
/// required method for designer support – do not modify
/// the contents of this method with the code editor.
/// </summary>
private void initializecomponent()
{
this.components = new system.componentmodel.container();
this.size = new size(800,600);
this.text = “form1”;
}
#endregion
/// <summary>
/// the main entry point for the application.
/// </summary>
static void
main()
{
using (form1 frm = new form1())
{
// show our form and initialize our graphics engine
frm.show();
frm.initializegraphics();
application.run(frm);
}
}
}
}