Методика проектирования неподвижных и подвижных плоских фигур
Листинг 44.1. Код для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
if (!myForm1.InitializeDirectX())
{
MessageBox.Show("Ошибка при инициализации DirectX.");
return;
}
//Показываем форму Form1:
myForm1.Show();
//Рисуем графику на форме Form1:
while (myForm1.Created)
{
myForm1.myRendering();
Application.DoEvents();
}
}
Для закрытия Form1 после щелчка кнопки Cancel дважды щелкаем эту кнопку. Появляется файл Form1.cs с шаблоном, в который записываем (Close()).
Для вывода справочной Form2 (после щелчка кнопки Help на Form1) дважды щелкаем эту кнопку. Появляется файл Form1.cs с шаблоном, в который записываем:
Form2 myForm2 = new Form2(); myForm2.Show(); (44.1)
Для ввода в проект новой формы в меню Project выбираем Add Windows Form, в панели Add New Item в окне Templates выделяем Windows Form, в окне Name оставляем имя Form2 и щелкаем кнопку Add (или Open для иной версии VS). Появляется Form2, в которую записываем (если нужно) справочную информацию. Вывод следующей формы (если в этом есть необходимость) после щелчка кнопки Next>> (или предыдущей формы после щелчка кнопки <<Back) осуществляется аналогично при помощи кода (44.1) с номером соответствующей формы.
Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для связывания формы Form1 с Direct3D.
Листинг 44.2. Методы для визуализации преобразований.
//Объявляем и инициализируем глобальную переменную
//для устройства myDevice класса Device:
Device myDevice = null;
//Устанавливаем параметры Direct3D:
public bool InitializeDirectX()
{
try
{
PresentParameters myPresentParameters =
new PresentParameters();
myPresentParameters.Windowed = true;
myPresentParameters.SwapEffect = SwapEffect.Discard;
myDevice = new Device(0, DeviceType.Hardware,
Открываем файл 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);
}
//Задаем параметры устройству:
this, CreateFlags.SoftwareVertexProcessing,
myPresentParameters);
return true;
}
catch (DirectXException)
{
return false;
}
}
//Метод для визуализации преобразований и построения графики:
public void myRendering()
{
if (myDevice == null)
return;
//Очищаем и заливаем белым цветом устр-во в виде Form1:
myDevice.Clear(ClearFlags.Target,
System.Drawing.Color.White, 1.0f, 0);
//Начинаем сцену:
myDevice.BeginScene();
//Заканчиваем сцену:
myDevice.EndScene();
myDevice.Present();
}
// Чтобы закрыть Form1 после нажатия клавиши Esc:
protected override void OnKeyPress(
System.Windows.Forms.KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar ==
(int)System.Windows.Forms.Keys.Escape)
this.Close();
}
Листинг 44.3. Методы для визуализации преобразованных вершин фигуры.
//Объявляем и инициализируем глобальные переменные:
Device myDevice = null;
VertexBuffer myVertexBuffer = null;
//Устанавливаем параметры Direct3D:
public bool InitializeDirectX()
{
try
{
PresentParameters myPresentParameters =
new PresentParameters();
myPresentParameters.Windowed = true;
myPresentParameters.SwapEffect = SwapEffect.Discard;
myDevice = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
myPresentParameters);
this.OnCreateDevice(myDevice, null);
return true;
}
catch (DirectXException)
{
return false;
}
}
//Создаем массив вершин фигуры:
public void OnCreateDevice(object sender, EventArgs e)
{
//Создаем буфер для 3-x вершин треугольника:
myVertexBuffer = new VertexBuffer(
typeof(CustomVertex.TransformedColored), 3,
myDevice, 0, CustomVertex.TransformedColored.Format,
Pool.Default);
myVertexBuffer.Created += new System.EventHandler(
this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Задаем параметры вершин:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
GraphicsStream myGraphicsStream = myVertexBuffer.Lock(0, 0, 0);
CustomVertex.TransformedColored[] Vertex =
new CustomVertex.TransformedColored[3];
//Вершина 0:
Vertex[0].X = 150; Vertex[0].Y = 50; Vertex[0].Z = 0.5f;
Vertex[0].Rhw = 1;
Vertex[0].Color = System.Drawing.Color.Aqua.ToArgb();
//Вершина 1:
Vertex[1].X = 250; Vertex[1].Y = 300; Vertex[1].Z = 0.5f;
Vertex[1].Rhw = 1;
Vertex[1].Color = System.Drawing.Color.Black.ToArgb();
//Вершина 2:
Vertex[2].X = 50; Vertex[2].Y = 300; Vertex[2].Z = 0.5f;
Vertex[2].Rhw = 1;
Vertex[2].Color = System.Drawing.Color.LightPink.ToArgb();
myGraphicsStream.Write(Vertex);
myVertexBuffer.Unlock();
}
//Метод для начала и окончания визуализации
//преобразованных вершин:
public void myRendering()
{
if (myDevice == null)
return;
//Задаем белый цвет (Color.White) форме Form1:
myDevice.Clear(ClearFlags.Target,
System.Drawing.Color.White, 1.0f, 0);
//Начинаем сцену:
myDevice.BeginScene();
myDevice.SetStreamSource(0, myVertexBuffer, 0);
myDevice.VertexFormat = CustomVertex.TransformedColored.Format;
myDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
//Заканчиваем сцену:
myDevice.EndScene();
myDevice.Present();
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код со следующего листинга (для вывода формы Form1 и рисования на ней графики).
Листинг 44.4. Код для вывода формы и рисования на ней графики.
using (Form1 myForm1 = new Form1())
{
if (!myForm1.InitializeDirectX())
{
MessageBox.Show("Ошибка при инициализации DirectX.");
return;
}
//Показываем форму Form1:
myForm1.Show();
//Рисуем графику на форме Form1:
while (myForm1.Created)
{
myForm1.myRendering();
Application.DoEvents();
}
}
Листинг 44.5. Метод для фотографирования клиентской области формы.
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern long BitBlt(IntPtr hdcDest,
int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
private Bitmap myMemoryImage;
private void myCaptureScreen()
{
Graphics myGraphics = this.CreateGraphics();
Size s = this.Size;
myMemoryImage = new Bitmap(s.Width, s.Height,
myGraphics);
Graphics myMemoryGraphics =
Graphics.FromImage(myMemoryImage);
IntPtr dc0 = myGraphics.GetHdc();
IntPtr dc1 = myMemoryGraphics.GetHdc();
BitBlt(dc1, 0, 0, this.ClientRectangle.Width,
this.ClientRectangle.Height,
dc0, 0, 0, 13369376);
myGraphics.ReleaseHdc(dc0);
myMemoryGraphics.ReleaseHdc(dc1);
}
Если мы забыли разместить компоненты PrintDocument и PrintDialog, то размещаем их сейчас и дважды щелкаем по значку для компонента PrintDocument. Открывается файл Form1.cs с шаблоном, который после записи одной строки нашего кода (для рисования в памяти сфотографированного выше изображения) имеет такой вид.
Листинг 44.6. Код для рисования изображения в памяти компьютера.
private void printDocument1_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
e.Graphics.DrawImage(myMemoryImage, 0, 0);
}
Теперь дважды щелкаем по кнопке Print (рис. 44.7) в режиме проектирования. Открывается файл Form1.cs с автоматически сгенерированным шаблоном обработчика щелчка по кнопке, и этот шаблон после записи нашего кода принимает такой вид.
Листинг 44.7. Код для печати изображения на принтере.
private void button1_Click(object sender, EventArgs e)
{
//Вызываем метод для захвата изображения:
myCaptureScreen();
// Передаем объекту printDialog1 информацию об объекте
//printDocument1 при помощи свойства Document:
printDialog1.Document = printDocument1;
//Выводим стандартную панель Print при помощи метода
//ShowDialog() для задания параметров печати
//и после щелчка OK на панели Print печатаем документ
//при помощи метода Print():
if (printDialog1.ShowDialog() == DialogResult.OK)
printDocument1.Print();
}
Последнюю строку кода можно записать также в более полном (и более понятном, но более длинном) виде:
System.Windows.Forms.DialogResult result =
printDialog1.ShowDialog();
if (result == DialogResult.OK)
printDocument1.Print();
Открываем файл Form1.cs (например, по схеме: File, Open, File) и выше пространства имен с именем нашего проекта (namespace Visual_DirectX_n3) записываем директивы для подключения этих же двух пространств имен:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
Коды для обработки щелчков по всем кнопкам на форме Form1 (рис. 44.8), а также для клавиши Esc приведены в предыдущем параграфе. Теперь в любом месте файла Form1.cs (например, ниже предыдущих методов для обработки щелчков по кнопкам) записываем следующие методы для выполнения преобразований вершин треугольника и прямоугольника и визуализации этих преобразований.
Листинг 44.8. Методы для визуализации преобразованных вершин фигур.
//Объявляем и инициализируем глобальные переменные.
//Общее устройство для всех фигур:
Device myDevice = null;
//Объявляем буфер вершин для треугольника:
VertexBuffer myVertexBuffer1 = null;
//Объявляем буфер вершин для прямоугольника:
VertexBuffer myVertexBuffer2 = null;
//Устанавливаем параметры Direct3D:
public bool InitializeDirectX()
{
try
{
//Для треугольника:
PresentParameters myPresentParameters1 =
new PresentParameters();
myPresentParameters1.Windowed = true;
myPresentParameters1.SwapEffect = SwapEffect.Discard;
myDevice = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
myPresentParameters1);
this.OnCreateDevice(myDevice, null);
//Для прямоугольника:
PresentParameters myPresentParameters2 =
new PresentParameters();
myPresentParameters2.Windowed = true;
myPresentParameters2.SwapEffect = SwapEffect.Discard;
myDevice = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
myPresentParameters2);
this.OnCreateDevice(myDevice, null);
return true;
}
catch (DirectXException)
{
return false;
}
}
//Метод для начала и окончания визуализации
//преобразованных вершин:
public void myRendering()
{
if (myDevice == null)
return;
myDevice.Clear(ClearFlags.Target,
System.Drawing.Color.White, 1.0f, 0);
myDevice.VertexFormat =
CustomVertex.TransformedColored.Format;
//Начинаем сцену:
myDevice.BeginScene();
//Для треугольника:
myDevice.SetStreamSource(0, myVertexBuffer1, 0);
myDevice.DrawPrimitives(PrimitiveType.TriangleList,0, 1);
//Для прямоугольника:
myDevice.SetStreamSource(0, myVertexBuffer2, 0);
myDevice.DrawPrimitives(PrimitiveType.TriangleStrip,0,2);
//Заканчиваем сцену:
myDevice.EndScene();
myDevice.Present();
}
//Создаем устройство и два буфера для вершин фигур:
public void OnCreateDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
//Создаем буфер для вершин треугольника:
myVertexBuffer1 = new VertexBuffer(
typeof(CustomVertex.TransformedColored), 3,
myDev, 0, CustomVertex.TransformedColored.Format,
Pool.Default);
myVertexBuffer1.Created +=
new System.EventHandler(this.OnCreateVertexBuffer1);
this.OnCreateVertexBuffer1(myVertexBuffer1, null);
// Создаем буфер для вершин четырехугольника:
myVertexBuffer2 = new VertexBuffer(
typeof(CustomVertex.TransformedColored), 4,
myDev, 0, CustomVertex.TransformedColored.Format,
Pool.Default);
myVertexBuffer2.Created += new System.EventHandler(
this.OnCreateVertexBuffer2);
this.OnCreateVertexBuffer2(myVertexBuffer2, null);
}
//Задаем параметры вершин треугольника:
public void OnCreateVertexBuffer1(object sender, EventArgs e)
{
VertexBuffer myVB1 = (VertexBuffer)sender;
GraphicsStream myGraphicsStream1 = myVB1.Lock(0, 0, 0);
CustomVertex.TransformedColored[] Vertex1 =
new CustomVertex.TransformedColored[3];
//Вершина 0:
Vertex1[0].X = 150; Vertex1[0].Y = 50; Vertex1[0].Z=0.5f;
Vertex1[0].Rhw = 1;
Vertex1[0].Color = System.Drawing.Color.Aqua.ToArgb();
//Вершина 1:
Vertex1[1].X = 250; Vertex1[1].Y =300; Vertex1[1].Z=0.5f;
Vertex1[1].Rhw = 1;
Vertex1[1].Color = System.Drawing.Color.Black.ToArgb();
//Вершина 2:
Vertex1[2].X = 50; Vertex1[2].Y = 300; Vertex1[2].Z=0.5f;
Vertex1[2].Rhw = 1;
Vertex1[2].Color =
System.Drawing.Color.LightPink.ToArgb();
myGraphicsStream1.Write(Vertex1);
myVB1.Unlock();
}
//Задаем параметры вершин прямоугольника:
public void OnCreateVertexBuffer2(object sender, EventArgs EvArgs)
{
VertexBuffer myVB2 = (VertexBuffer)sender;
GraphicsStream myGraphicsStream2 = myVB2.Lock(0, 0, 0);
CustomVertex.TransformedColored[] Vertex2 =
new CustomVertex.TransformedColored[4];
//Вершина 0:
Vertex2[0].X = 300.0f; Vertex2[0].Y = 300.0f;
Vertex2[0].Z = 0.5f; Vertex2[0].Rhw = 1;
Vertex2[0].Color = System.Drawing.Color.Black.ToArgb();
//Вершина 1:
Vertex2[1].X = 300.0f; Vertex2[1].Y = 50.0f;
Vertex2[1].Z = 0.5f; Vertex2[1].Rhw = 1;
Vertex2[1].Color = System.Drawing.Color.White.ToArgb();
//Вершина 2:
Vertex2[2].X = 500.0f; Vertex2[2].Y = 300.0f;
Vertex2[2].Z = 0.5f; Vertex2[2].Rhw = 1;
Vertex2[2].Color = System.Drawing.Color.Blue.ToArgb();
//Вершина 3:
Vertex2[3].X = 500.0f; Vertex2[3].Y = 50.0f;
Vertex2[3].Z = 0.5f; Vertex2[3].Rhw = 1;
Vertex2[3].Color = System.Drawing.Color.Green.ToArgb();
myGraphicsStream2.Write(Vertex2);
myVB2.Unlock();
}
Листинг 44.9. Методы для визуализации преобразованных вершин фигуры.
//Глобальные переменные:
Device myDevice = null; // Our rendering device
VertexBuffer myVertexBuffer = null;
PresentParameters myPresentParameters =
new PresentParameters();
bool myPause = false;
//Задаем параметры DirectX:
public bool InitializeDirectX()
{
try
{
myPresentParameters.Windowed = true;
myPresentParameters.SwapEffect = SwapEffect.Discard;
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)
{
return false;
}
}
//Создаем буфер вершин для прямоугольника:
public void OnCreateDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
//Задаем 4 вершины прямоугольника:
myVertexBuffer = new VertexBuffer(
typeof(CustomVertex.PositionColored), 4, myDev, 0,
CustomVertex.PositionColored.Format, Pool.Default);
//Создаем геом- е данные при обработке события Created:
myVertexBuffer.Created += new System.EventHandler(
this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(myVertexBuffer, null);
}
//Настраиваем параметры Direct3D:
public void OnResetDevice(object sender, EventArgs e)
{
Device myDev = (Device)sender;
// Выключаем режим CullMode, чтобы мы видели
//переднюю и заднюю поверхность фигуры:
myDev.RenderState.CullMode = Cull.None;
//Выключаем освещение Direct3D, так как мы задали
//наши собственные цвета в вершинах:
myDev.RenderState.Lighting = false;
}
//Создаем буфер вершин фигуры:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
//Для 4-х вершин прямоугольника:
VertexBuffer myVB = (VertexBuffer)sender;
CustomVertex.PositionColored[] Vertex = (
CustomVertex.PositionColored[])myVB.Lock(0, 0);
//Вершина 0:
Vertex[0].X = -1.0f; Vertex[0].Y = -1.0f;
Vertex[0].Z = 0.0f;
Vertex[0].Color = System.Drawing.Color.Black.ToArgb();
//Вершина 1:
Vertex[1].X = -1.0f; Vertex[1].Y = 1.0f;
Vertex[1].Z = 0.0f;
Vertex[1].Color =
System.Drawing.Color.MediumOrchid.ToArgb();
//Вершина 2:
Vertex[2].X = 1.0f; Vertex[2].Y = -1.0f;
Vertex[2].Z = 0.0f;
Vertex[2].Color = System.Drawing.Color.Black.ToArgb();
//Вершина 3:
Vertex[3].X = 1.0f; Vertex[3].Y = 1.0f;
Vertex[3].Z = 0.0f;
Vertex[3].Color = System.Drawing.Color.Cornsilk.ToArgb();
myVB.Unlock();
}
//Выполняем визуализацию преобразованных вершин:
public void myRendering()
{
if (myDevice == null) return;
if (myPause) return;
//Очищаем и заливаем форму Form1 белым цветом:
myDevice.Clear(ClearFlags.Target,
System.Drawing.Color.White, 1.0f, 0);
//Начинаем сцену:
myDevice.BeginScene();
//Используем матрицы для выполнения преобразований:
SetupMatrices();
myDevice.SetStreamSource(0, myVertexBuffer, 0);
myDevice.VertexFormat =
CustomVertex.PositionColored.Format;
//Для прямоугольника:
myDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
//Заканчиваем сцену:
myDevice.EndScene();
myDevice.Present();
}
//Включаем таймер и выполняем матричные преобразования:
private void SetupMatrices()
{
//Используем структуру матриц Matrix,
// чтобы вращать фигуру вокруг оси y.
//Один оборот на 2*PI радиан фигура совершит
//за 1000 мс (1 секунду):
int iTime = Environment.TickCount % 1000;
float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
myDevice.Transform.World = Matrix.RotationY(fAngle);
//Задаем координаты глаза наблюдателя
//в матрице вида (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, 1.0f, 1.0f, 100.0f);
}
//Останавливаем вращение фигуры
//во время изменения размеров формы Form1:
protected override void OnResize(System.EventArgs e)
{
myPause = ((this.WindowState ==
FormWindowState.Minimized) || !this.Visible);
}
Теперь в файле Form1.cs (или Program.cs) находим главный метод Main, комментируем весь имеющийся в этом методе автоматически сгенерированный код и записываем код с листинга предыдущего параграфа (для вывода формы Form1 и рисования на ней графики).
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 проектировать разнообразные анимированные объемные изображения.