Глава 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 и рисования на ней графики).


Наши рекомендации