Глава 45. Методика проектирования неподвижных и подвижных пространственных фигур
Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n5) записываем директивы для подключения пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
Листинг 45.1. Методы для визуализации преобразованных вершин фигуры.
//Глобальные переменные.
//Объявляем устройство для визуализации вершин:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
PresentParameters myPresentParameters =
new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
try
{
myPresentParameters.Windowed = true;
myPresentParameters.SwapEffect =
SwapEffect.Discard;
myPresentParameters.EnableAutoDepthStencil = true;
myPresentParameters.AutoDepthStencilFormat =
DepthFormat.D16;
//Создаем устройство для визуализации:
myDevice = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
myPresentParameters);
myDevice.DeviceReset +=
new System.EventHandler(this.OnResetDevice);
this.OnCreateDevice(myDevice, null);
this.OnResetDevice(myDevice, null);
myPause = false;
return true;
}
catch (DirectXException)
{
//Перехвачена ошибка инициализации DirectX:
return false;
}
}
//Создаем буфер вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
myVertexBuffer = new VertexBuffer(
typeof(CustomVertex.PositionNormal), 100, myDev,
Usage.WriteOnly, CustomVertex.PositionNormal.Format,
Pool.Default);
myVertexBuffer.Created += new System.EventHandler(
this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры устройству:
public void OnResetDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
//Выключаем режим CullMode, чтобы видеть
//внутреннюю и наружную поверхности фигуры:
myDevice.RenderState.CullMode = Cull.None;
//Включаем Z - буфер:
myDevice.RenderState.ZBufferEnable = true;
//Делаем доступным освещение:
myDevice.RenderState.Lighting = true;
}
//Строим фигуру в буфере вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
VertexBuffer myVB = (VertexBuffer)sender;
//В структуре PositionNormal
//создаем массив из 100 пользовательских вершин:
CustomVertex.PositionNormal[] Vertex =
(CustomVertex.PositionNormal[])myVB.Lock(0, 0);
for (int i = 0; i < 50; i++)
{
//Заполняем вершины данными:
float theta = (float)(2 * Math.PI * i) / 49;
//Вращающийся конус с одной неподвижной вершиной.
//Рассчитываем координаты вершин:
Vertex[2 * i].Position =
new Vector3((float)Math.Sin(theta), -1,
(float)Math.Cos(theta));
//Рассчитываем нормали в вершинах:
Vertex[2 * i + 1].Normal =
new Vector3((float)Math.Sin(theta), -1,
(float)Math.Cos(theta));
}
//Открываем буфер вершин:
myVB.Unlock();
}
//Включаем таймер и выполняем матричные преобразования:
private void SetupMatrices()
{
//Используем глобальную матрицу (world matrix),
//чтобы вращать фигуру вокруг оси y:
myDevice.Transform.World = Matrix.RotationAxis(
new Vector3(
(float)Math.Cos(Environment.TickCount / 250.0f), 1,
(float)Math.Sin(Environment.TickCount / 250.0f)),
Environment.TickCount / 3000.0f);
//Задаем координаты глаза наблюдателя
//в матрице вида (view matrix):
myDevice.Transform.View = Matrix.LookAtLH(
new Vector3(0.0f, 3.0f, -5.0f),
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f));
//При помощи матрицы проецирования (projection matrix)
//выполняем перспективные преобразования:
myDevice.Transform.Projection =
Matrix.PerspectiveFovLH(
(float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f);
}
//Определяем освещение фигуры цветом формата ARGB:
private void SetupLights()
{
//Устанавливаем материал и его цвет.
//Можно одновременно использовать только один материал:
Material myMaterial = new Material();
Color myColor = Color.White;
myMaterial.Diffuse = myColor;
myMaterial.Ambient = myColor;
myDevice.Material = myMaterial;
//Устанавливаем белое освещение
//с изменяющимся направлением:
myDevice.Lights[0].Type = LightType.Directional;
myDevice.Lights[0].Diffuse = Color.DarkTurquoise;
myDevice.Lights[0].Direction = new Vector3(
(float)Math.Cos(Environment.TickCount / 250.0f), 1.0f,
(float)Math.Sin(Environment.TickCount / 250.0f));
//Включаем освещение:
myDevice.Lights[0].Enabled = true;
//Включаем немного отраженного (Ambient)
//равномерно рассеянного света:
myDevice.RenderState.Ambient = Color.FromArgb(0x202020);
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
if (myPause) return;
//Очищаем и заливаем форму Form1 белым цветом:
myDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
Color.White, 1.0f, 0);
//Начинаем сцену:
myDevice.BeginScene();
//Устанавливаем освещение и матерал:
SetupLights();
//Задаем матрицы (world, view, projection):
SetupMatrices();
myDevice.SetStreamSource(0, myVertexBuffer, 0);
myDevice.VertexFormat =
CustomVertex.PositionNormal.Format;
//Рисуем фигуру:
myDevice.DrawPrimitives(
PrimitiveType.TriangleStrip, 0, (4 * 25) - 2);
//Заканчиваем сцену:
myDevice.EndScene();
//Обновляем экран:
myDevice.Present();
}
//Останавливаем вращение фигуры
//во время изменения размеров формы Form1:
protected override void OnResize(System.EventArgs e)
{
myPause = ((this.WindowState ==
FormWindowState.Minimized) || !this.Visible);
}
//Закрываем форму Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar == (int)Keys.Escape)
this.Close();
}
Листинг 45.2. Код для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
if (!myForm1.InitializeDirectX())
{
MessageBox.Show("Ошибка при инициализации DirectX.");
return;
}
//Показываем форму Form1:
myForm1.Show();
//Рисуем графику на форме Form1:
while (myForm1.Created)
{
myForm1.myRendering();
Application.DoEvents();
}
}
Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n6) записываем директивы для подключения пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
Коды для обработки щелчков по всем кнопкам на форме Form1 (рис. 45.4) приведены выше в этой части книги.
Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для выполнения матричных преобразований вершин фигуры (с текстуры) и визуализации этих преобразований в динамике.
Листинг 45.3. Методы для визуализации преобразованных вершин фигуры.
//Глобальные переменные.
//Объявляем устройство для визуализации вершин:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
//Объявляем и инициализируем объект myTexture
//класса Texture:
Texture myTexture = null;
PresentParameters myPresentParameters =
new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
try
{
myPresentParameters.Windowed = true;
myPresentParameters.SwapEffect = SwapEffect.Discard;
myPresentParameters.EnableAutoDepthStencil = true;
myPresentParameters.AutoDepthStencilFormat =
DepthFormat.D16;
//Создаем устройство для визуализации:
myDevice = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
myPresentParameters);
myDevice.DeviceReset +=
new System.EventHandler(this.OnResetDevice);
this.OnCreateDevice(myDevice, null);
this.OnResetDevice(myDevice, null);
myPause = false;
return true;
}
catch (DirectXException)
{
//Перехвачена ошибка инициализации DirectX:
return false;
}
}
//Создаем буфер вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
myVertexBuffer = new VertexBuffer(
typeof(CustomVertex.PositionNormalTextured), 100,
myDev, Usage.WriteOnly,
CustomVertex.PositionNormalTextured.Format,
Pool.Default);
myVertexBuffer.Created +=
new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры устройству:
public void OnResetDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
//Выключаем режим CullMode, чтобы видеть
//внутреннюю и наружную поверхности фигуры:
myDev.RenderState.CullMode = Cull.None;
//Выключаем трехмерное освещение:
myDev.RenderState.Lighting = false;
//Включаем Z - буфер (ZBuffer):
myDev.RenderState.ZBufferEnable = true;
//Создаем нашу текстуру.
//Загружаем на поверхность фигуры наш рисунок Texture_1
//в виде текстуры:
myTexture = TextureLoader.FromFile(myDev,
Application.StartupPath + @"\..\..\Texture_1.bmp");
}
//Строим фигуру в буфере вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
VertexBuffer myVB = (VertexBuffer)sender;
//В структуре PositionNormalTextured
//создаем массив из 100 пользовательских вершин:
CustomVertex.PositionNormalTextured[] Vertex =
(CustomVertex.PositionNormalTextured[])myVB.Lock(0, 0); // Lock the buffer (which will return our structs)
for (int i = 0; i < 50; i++)
{
//Заполняем вершины данными:
float theta = (float)(2 * Math.PI * i) / 49;
//Рассчитываем нормали в вершинах:
Vertex[2 * i].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
//Добавляем в вершину v-компоненту текстуры:
Vertex[2 * i].Tv = 1.0f;
//Рассчитываем координаты вершин:
Vertex[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta));
//Добавляем в вершину u-компоненту текстуры:
Vertex[2 * i + 1].Tu = ((float)i) / (50 - 1);
}
//Открываем буфер вершин:
myVB.Unlock();
}
//Включаем таймер и выполняем матричные преобразования:
private void SetupMatrices()
{
//Используем глобальную матрицу (world matrix),
//чтобы вращать фигуру вокруг оси y.
myDevice.Transform.World = Matrix.RotationAxis(
new Vector3(
(float)Math.Cos(Environment.TickCount / 250.0f), 1,
(float)Math.Sin(Environment.TickCount / 250.0f)),
Environment.TickCount / 1000.0f);
//Задаем координаты глаза наблюдателя
//в матрице вида (view matrix):
myDevice.Transform.View = Matrix.LookAtLH(
new Vector3(0.0f, 3.0f, -5.0f),
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f));
//При помощи матрицы проецирования (projection matrix)
//выполняем перспективные преобразования:
myDevice.Transform.Projection =
Matrix.PerspectiveFovLH(
(float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f);
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
if (myPause) return;
//Очищаем и заливаем форму Form1 белым цветом:
myDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
Color.White, 1.0f, 0);
//Начинаем сцену:
myDevice.BeginScene();
//Задаем матрицы (world, view, projection):
SetupMatrices();
//Устанавливаем нашу текстуру:
myDevice.SetTexture(0, myTexture);
myDevice.TextureState[0].ColorOperation =
TextureOperation.Modulate;
myDevice.TextureState[0].ColorArgument1 =
TextureArgument.TextureColor;
myDevice.TextureState[0].ColorArgument2 =
TextureArgument.Diffuse;
myDevice.TextureState[0].AlphaOperation =
TextureOperation.Disable;
//Рисуем фигуру:
myDevice.SetStreamSource(0, myVertexBuffer, 0);
myDevice.VertexFormat =
CustomVertex.PositionNormalTextured.Format;
myDevice.DrawPrimitives(
PrimitiveType.TriangleStrip, 0, (4 * 25) - 2);
//Заканчиваем сцену:
myDevice.EndScene();
//Обновляем экран:
myDevice.Present();
}
//Останавливаем вращение фигуры
//во время изменения размеров формы Form1:
protected override void OnResize(System.EventArgs e)
{
myPause = ((this.WindowState ==
FormWindowState.Minimized) || !this.Visible);
}
//Закрываем форму Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar == (int)Keys.Escape)
this.Close();
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код со следующего листинга.
Листинг 45.4. Код для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
if (!myForm1.InitializeDirectX())
{
MessageBox.Show("Ошибка при инициализации DirectX.");
return;
}
//Показываем форму Form1:
myForm1.Show();
//Рисуем графику на форме Form1:
while (myForm1.Created)
{
myForm1.myRendering();
Application.DoEvents();
}
}
По этой методологии можно в проектах Visual C# при помощи DirectX проектировать разнообразные анимированные объемные изображения.