Глава 44. Методика проектирования неподвижных и подвижных плоских фигур
Листинг 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,
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 и рисования на ней графики).