Глава 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 проектировать разнообразные анимированные объемные изображения.


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