上一次中途結束了本來應該講到的控制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);
}
}
}
}
新聞熱點
疑難解答