Меш (Mesh)
Mesh - в прямом переводе - сетчатая структура используется следующим образом:
1) один из поддерживаемых в DirectX форматов - X-File с расширением .x.
2) Direct3D-Класс из Microsoft.DirectX.Direct3DX, который загружает и отрисовывает такие .x-Файлы.
Простые Меши
Direct3D работает с несколькими простейшими стандартными 3D-фигурами в своем Mesh-Классе, из которых можно составлять любые фигуры.
Их Вертексы типа CustomVertex.PositionNormal, могут быть освещены, но не содержат информации о цвете и текстурных координатах и поэтому не могут быть текстурированными. Эти Вертексы используют Material-свойства Device.
Пример:
static Mesh myPolygon = Mesh.Polygon ( device, 0.3f, 8 ); //создает многоугольник со стороной
// равной 0.3f и количеством
// симметричных верши - 8
static Mesh myBox = Mesh.Box ( device, 0.5f, 0.5f, 0.5f ); // параллелепипед со сторонами
// xSize = 0.5f, ySize = 0.5f, zSize = 0.5f
static Mesh mySphere = Mesh.Sphere ( device, 0.5f, 20, 20 ); //радиус 0.5f, слои вокруг оси,
// ярусы вдоль оси
static Mesh myTorus = Mesh.Torus ( device, 0.2f, 0.4f, 20, 20 );//внутренний и внешний радиусы,
// слои и ярусы
static Mesh myCylinder = Mesh.Cylinder( device, 0.5f, 0.2f, 0.8f, 20, 20 ); //радиусы,
//высота, слои+ярусы
static Mesh myTeapot = Mesh.Teapot ( device ); // классический чайник
static Mesh myText = Mesh.TextFromFont( device, new System.Drawing.Font(
FontFamily.GenericSerif, 12 )
, text, 0.01f, 0.25f );
//строка, сглаживание краев, толщина
// а теперь отрисуем все это
device.BeginScene();
myPolygon .DrawSubset( 0 );
myBox .DrawSubset( 0 );
mySphere .DrawSubset( 0 );
myTorus .DrawSubset( 0 );
myCylinder.DrawSubset( 0 );
myTeapot .DrawSubset( 0 );
myText .DrawSubset( 0 );
device.EndScene();
X-Files
X-Файловый формат разработан в Microsoft и стал, также как и BMP-Формат для растровых картинок, с течением времени универсальным и популярным. Существует в двух видах a) компактный, но нечитаемый бинарный формат и b) как редактируемый текстовый формат.
Описание формата можно найти тут: paulbourke.net/dataformats/.
Практически их любого редактора 3D-Моделирования можно экспортировать X-Files:
Maya 5 und Maya 6 Plug-Ins после установки DirectX SDK: C:\DXSDK\Utilities\Bin\plugins\Maya
Adobe Photoshop Plug-In после установки DirectX SDK: C:\DXSDK\Utilities\Bin\plugins\Photoshop
Autodesk 3ds Max Plug-In : www.andytather.co.uk/Panda/directxmax_downloads.aspx
AC3D : www.inivis.com/
LightWave 3D siehe: https://www.lightwave3d.com/
MeshX : www.spinnerbaker.com/meshx.htm
MilkShape 3D : http://www.milkshape3d.com/
X-File Коллекция: www.3dcafe.com
Коллекция полезных статей : sites.google.com/site/craigandera/craigs-stuff/directx-home
Мы можем сами написать X-File в текстовом формате - это муторно, но поучительно.
Пример одного .x-Файла :
xof 0302txt 0064 //mandatory X-file header Mesh { -1.0; 1.0; 0.0;,//p0 9; //vertex count | |
Скопируйте этот текст в текстовой файл C:\temp\mesh.x.
Обратите внимание:
1) .x-Файлы должны начинаться сточкой xof 0302txt 0064.
2) Затем .x-Файлы содержат обычно так называемые Templates = синтаксический описатель блоков. Эти Templates необязательные части файла и мы их отбросили.
3) Блок Mesh содержит Vertex- и Index-Buffer и два подблока MeshVertexColors и MeshMaterialList, где последний обязателен для присутствия, но в нашем случае не имеет смысла.
4) за знаком // начинаются комментарии, как в обычном коде.
5) Каждый блок начинается с целого числа = количество следующих элементов в данном блоке.
6) Элементы отделяются запятой, строки отделяются точкой с запятой. После последнего элемента следует дополнительная точка с запятой.
7) Каждый элемент IndexBuffer-а использует index count (в примере: 3; в левой колонке).
8) Каждый элемент MeshVertexColors-блок использует вертексный номер (в примере: 0; ... 8; в левой колонке).
9) Компоненты цвета R, G, B и прозрачность кодируются значениями от 0.0 = отсутствие и 1.0 = полное присутствие.
10) Написание 0.0 и 0 для типа float одинаковы по значению, в отличии от 1.0 и 1.
11) Маленькие синтаксические ошибки сделают Файл нерабочим, и эти ошибки тяжело найти, поэтому аккуратное структурированное написание крайне важно.
Загрузка Mesh-а
C#-Programm mesh1:
//Form1.cs **************************************************************************
//Add References: Microsoft.DirectX, Microsoft.Direct3D,
// Microsoft.Direct3DX, Vers. >= 1.0.2902.0
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
public class Form1 : Form {
[STAThread] static void Main() {
Application.Run( new Form1() );
}
static Device device;
static Mesh mymesh;
static float fAngle;
PresentParameters presentParams;
Timer myTimer = new Timer();
public Form1() {
myTimer.Tick += new EventHandler( OnTimer );
myTimer.Interval = 1;
ClientSize = new Size( 400, 300 );
}
protected override void OnResize( System.EventArgs e ) {
myTimer.Stop();
try {
presentParams = new PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
if ( device != null ) device.Dispose();
device = new Device( 0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams );
if ( mymesh != null ) mymesh.Dispose();
try {
mymesh = Mesh.FromFile( @"C:\temp\mesh.x", MeshFlags.SystemMemory, device );
} catch {
MessageBox.Show( @"Missing or Invalid: C:\temp\mesh.x" );
return;
}
device.Transform.View = Matrix.LookAtLH( new Vector3( 0f,0f,-4f ),
new Vector3( 0f,0f,0f ),
new Vector3( 0f,1f,0f ) );
device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4, 1f, 1f, 10f );
device.RenderState.CullMode = Cull.None;
device.RenderState.Ambient = Color.White;
device.RenderState.AmbientMaterialSource = ColorSource.Color1;
myTimer.Start();
} catch (DirectXException ex) {
MessageBox.Show( ex.ToString() );
return;
}
}
protected static void OnTimer( Object myObject, EventArgs myEventArgs ) {
if (device == null) return;
device.Clear( ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1f, 0 );
device.Transform.World = Matrix.RotationY( fAngle += 0.01f );
device.BeginScene();
mymesh.DrawSubset(0);
device.EndScene();
device.Present();
}
}
Данная программа не создает Вертексов, Индексов и Цветов, а берет все это из .x-Файла C:\temp\mesh.x.
Вы должны подключить соответствующие библиотеки Microsoft.DirectX, Microsoft.DirectX.Direct3D и Microsoft.DirectX.Direct3DX.
ExtendedMaterials
a) В MeshMaterialList можно вставить в Material-блок какую-нибудь текстуру. В этом случае .x-Файл должен содержать дополнительные текстурные координаты.
MeshMaterialList {
1;1;0;;
Material {
1.0;1.0;1.0;0;;0;0;0;0;;0;0;0;;
TextureFilename { "C:\\temp\\bmp1.bmp"; }
}
}
MeshTextureCoords {
4;
0.0, 0.0; //left upper corner
1.0, 0.0; //rigth upper corner
0.0, 1.0; //left lower corner
1.0, 1.0;;//right lower corner
}
б) В блоке MeshMaterialList могут содержаться два и более Material-блока. В этом случае ExtendedMaterials должны загружаться по-одному, в static Material[]- и static Texture[]-Массивы копироваться и отрисовываться.
Пример с 4-мя четырехугольниками и 2-мя ExtendedMaterials:
Если вы хотите повторить пример, тогда загрузите эти исходные картинки в директорию C:\temp:
C:\temp\bmp1.bmp C:\temp\bmp2.bmp | von mesh.x geliefertes Ergebnis |
Измененный х-файл:
xof 0302txt 0064 //mandatory X-file header
Mesh {
//VertexBuffer
9; //vertex count
-1.0; 1.0; 0.0;, //p0
0.0; 1.0; 0.0;, //p1
0.0; 0.0; 0.0;, //p2
-1.0; 0.0; 0.0;, //p3
0.0;-1.0; 0.0;, //p4
-1.0;-1.0; 0.0;, //p5
0.0; 1.0; 1.0;, //p6
0.0; 0.0; 1.0;, //p7
0.0;-1.0; 1.0;; //p8
//IndexBuffer
4; //face count
4; 0, 1, 2, 3;, //quad0
4; 3, 2, 4, 5;, //quad1
4; 1, 6, 7, 2;, //quad2
4; 2, 7, 8, 4;; //quad3
MeshTextureCoords { //each pi needs a ti
9;
0.0, 0.0; //t0 → upper left
1.0, 0.0; //t1 → upper rigth
1.0, 1.0; //t2 → lower right
0.0, 1.0; //t3 → lower left
1.0, 0.0; //t4 = t1 → mirror down
0.0, 0.0; //t5 = t0 → mirror down
0.0, 0.0; //t6 = t0 → mirror right
0.0, 1.0; //t7 = t3 → mirror right
0.0, 0.0;;//t8 = t0 → mirror down and right
} MeshMaterialList {
2;4;0,1,1,0;; //2 mats for 4 faces: mat 0 for faces 0,3,
//mat 1 for faces 1,2
Material { 1.0;1.0;1.0;0;;0;0;0;0;;0;0;0;;
TextureFilename { "C:\\temp\\bmp1.bmp"; }
}
Material { 1.0;1.0;1.0;0;;0;0;0;0;;0;0;0;;
TextureFilename { "C:\\temp\\bmp2.bmp"; }
}
}
}
//end of Mesh file C:\temp\mesh.x *********************************************************
//C#-Program: Form1.cs ********************************************************************
//Add References: Microsoft.DirectX, Microsoft.Direct3D, Microsoft.Direct3DX,
//Vers. >= 1.0.2902.0
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
public class Form1 : Form {
[STAThread] static void Main() {
Application.Run( new Form1() );
}
static Device device;
static Mesh mymesh;
static float fAngle;
ExtendedMaterial[] exmat; //new !
static Material [] mat; //new !
static Texture [] tex; //new !
PresentParameters presentParams;
Timer myTimer = new Timer();
public Form1() {
Text = "mesh1";
myTimer.Tick += new EventHandler( OnTimer );
myTimer.Interval = 1;
ClientSize = new Size( 400, 300 );
}
protected override void OnResize( System.EventArgs e ) {
myTimer.Stop();
try {
presentParams = new PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
if ( device != null ) device.Dispose();
device = new Device( 0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams );
if ( mymesh != null ) mymesh.Dispose();
try { mymesh = Mesh.FromFile( @"C:\temp\mesh.x", MeshFlags.SystemMemory,
device, out exmat );
} catch {
MessageBox.Show( @"Missing or Invalid: C:\temp\mesh.x" );
return;
}
if ( exmat != null && exmat.Length > 0 ) {
mat = new Material[exmat.Length];
tex = new Texture [exmat.Length];
for ( int i=0; i < exmat.Length; i++ )
{
mat[i].Ambient = exmat[i].Material3D.Diffuse;
if ( exmat[i].TextureFilename != null )
try { tex[i] = TextureLoader.FromFile( device, exmat[i].TextureFilename );
} catch {
MessageBox.Show( "Missing: " + exmat[i].TextureFilename ); return;
}
}
}
device.Transform.View = Matrix.LookAtLH( new Vector3( 0f,2f,-3f ),
new Vector3( 0f,0f,0f ),
new Vector3( 0f,1f,0f ) );
device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4,
1f, 1f, 10f );
device.RenderState.CullMode = Cull.None;
device.RenderState.Ambient = Сolor.White;
device.RenderState.Lighting = true;
myTimer.Start();
} catch( DirectXException ex ) {
MessageBox.Show( ex.ToString() );
return;
}
}
protected static void OnTimer( Object myObject, EventArgs myEventArgs ) {
if (device == null) return;
device.Clear( ClearFlags.Target | ClearFlags.ZBuffer, Color.White, 1f, 0 );
device.Transform.World = Matrix.RotationY( fAngle += 0.01f );
device.BeginScene();
for ( int i=0; i < mat.Length; i++ ) {
device.Material = mat[i];
device.SetTexture( 0, tex[i] ); mymesh.DrawSubset( i );
}
device.EndScene();
device.Present();
}
}
Текстурированный Куб
Отрисовка куба достаточно проста : Mesh cube = Mesh.Box( device, 1f, 1f, 1f );
Однако запросы параметров Куба возвращают удивительные результаты:
int nv = cube.NumberVertices; возвращает количество вершин nv = 24 вместо ожидаемых 8-ми.
int nf = cube.NumberFaces; возвращает количество плоскостей nf = 12 вместо ожидаемых 6.
VertexFormats vf = cube.VertexFormat; возвращает Вертексный Формат vf = PositionNormal, вместо ожидаемого PositionNormalTextured.
Очевидно, что Mesh.Box кодирует каздую вершину трижды и каждый квадрат представляет в виде двух треугольников.
Mesh.Box , кроме того, не содержит текстурных координат и соответственно Mesh.Box не может быть покрыт текстурой.
Существует два решения этой проблемы:
1) Mesh.Box клонировать в виде копии с форматом вершин, дополненным текстурными координатами, "MyTexturedBox".
2) Записать х-файл cube.x, который самостоятельно определяет все Вершины, Фэйсы и Текстуры.
запись cube.x-файла:
Это задание не совсем простое, так как на каждой из 8-ми вершин висят три текстуры, но каждая вершина содержит только одну текстурную координату Tu,Tv в вертексном формате, например. PositionNormalTextured. Для покрытия 6-ти плоскостей требуется 6 текстур и каждая текстуратребует 4 текстурные координаты = всего 6*4 = 24 Текстурные координаты - отсюда становится понятно почему Mesh.Box содержит 24 вершины, а не 8.
Следствие: Из-за текстурных координат требуется программировать каждую вершину трижды, по одному разу на каждую плоскость (Фейс). Таким образом, для свободного наложения картинки на каждую плоскость куба потребуется 24 текстурированных вершины с соответствующей сложной нумерацией и большим количеством кода.
Во многих случаях такое свободное текстурирование необязательно. Если это не важно правильно ли ориентирована текстура или нет, то можно обойтись и кодированием 12-ти вершин. |
х-файл:
xof 0302txt 0064 //mandatory X-file header
Mesh { //VertexBuffer
12; //vertex count
0.0; 0.0; 0.0;, //p0 floor
1.0; 0.0; 0.0;, //p1
1.0; 0.0; 1.0;, //p2
0.0; 0.0; 1.0;, //p3
0.0; 1.0; 0.0;, //p4 ceiling
1.0; 1.0; 0.0;, //p5
1.0; 1.0; 1.0;, //p6
0.0; 1.0; 1.0;, //p7
1.0; 0.0; 1.0;, //p8 = p2 floor for texture
0.0; 0.0; 1.0;, //p9 = p3
1.0; 1.0; 1.0;, //p10= p6 ceiling for texture
0.0; 1.0; 1.0;, //p11= p7
//IndexBuffer
6; //face count
4; 0, 1, 5, 4;, //front face
4; 1, 2, 6, 5;, //right face
4; 2, 3, 7, 6;, //back face
4; 3, 0, 4, 7;, //left face
4; 0, 1, 8, 9;, //floor face
4; 4, 5,10,11;; //ceiling face
MeshTextureCoords {
12; 0.0, 1.0; //p0 floor
1.0, 1.0; //p1
0.0, 1.0; //p2
1.0, 1.0; //p3
0.0, 0.0; //p4 ceiling
1.0, 0.0; //p5
0.0, 0.0; //p6
1.0, 0.0; //p7
1.0, 0.0; //p8 floor
0.0, 0.0; //p9
1.0, 1.0; //p10 ceiling
0.0, 1.0; //p11
}
MeshMaterialList {
2;6;0,1,0,1,0,1;; //2 mats for 6 faces: mat 0 for faces 0,2,4, mat 1 for faces
1,3,5
Material {
1.0;1.0;1.0;0;;0;0;0;0;;0;0;0;;
TextureFilename { "C:\\temp\\bmp1.bmp"; }
}
Material {
1.0;1.0;1.0;0;;0;0;0;0;;0;0;0;;
TextureFilename { "C:\\temp\\bmp2.bmp"; }
}
}
} //end of Mesh file C:\temp\cube.x *************************************************************
Запишите этот Меш в C:\temp\cube.x, поменяйте в коде имя загружаемого файла и попробуйте загрузить и посмотреть cube.x.