Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 891 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 14:

Использование Mesh-объектов в DirectX

Загрузка Mesh-объектов из внешних файлов

Отображение заварочных чайников выглядит вполне реалистичным, но большинство Mesh -объектов создают художники и редко используются рассмотренные выше примитивы (куб, тор, сфера и т.д.). Существуют вспомогательные программы-утилиты, которые позволяют создавать и сохранять Mesh -объекты в файлах X -форматов. Затем класс Mesh с помощью своих методов может загружать в приложение эти объекты из файлов и управлять ими. В X -файл можно поместить все необходимые данные для реалистичного рендеринга объекта, включая вершинные и индексные данные, информацию о материале и текстуре.

Для быстрой и реалистичной обработки трехмерных графических объектов существуют файлы с программным кодом, который напрямую работает с графическим устройством. В этих файлах находится код, который написан на специальном языке, называемом HLSL ( High Level Shader Language - высокоуровневый язык программирования для построения теней). Программы, написанные на этом языке, называются шейдерами, а файлы, в которых хранятся шейдеры - файлами шейдеров.

Кроме статических методов создания графических примитивов (куб, тор, ...) в классе Mesh содержатся еще два статических метода Mesh.FromFile() и Mesh.FromStream(). Эти методы имеют большое количество перегрузок, но корневые перегрузки выглядят так

  1. public static Microsoft.DirectX.Direct3D.Mesh FromFile ( 
             System.String filename ,                       // Источник  данных 
             Microsoft.DirectX.Direct3D.MeshFlags options , // Как нужно загружать данные 
             Microsoft.DirectX.Direct3D.Device device ,     // Устройство 
             Microsoft.DirectX.Direct3D.GraphicsStream adjacency , // Смежные вершины 
             out Microsoft.DirectX.Direct3D.ExtendedMaterial[] materials , // Сохраненные материалы 
             Microsoft.DirectX.Direct3D.EffectInstance effects     // Файл шейдера для поддержки Mesh-объекта 
          )
  2. public static Microsoft.DirectX.Direct3D.Mesh FromStream ( 
             System.IO.Stream stream ,                             // Источник данных 
             System.Int32 readBytes ,                              // Сколько байт загружать из потока 
             Microsoft.DirectX.Direct3D.MeshFlags options ,        // Как нужно загружать данные 
             Microsoft.DirectX.Direct3D.Device device ,            // Устройство 
             Microsoft.DirectX.Direct3D.GraphicsStream adjacency , // Смежные вершины 
             out Microsoft.DirectX.Direct3D.ExtendedMaterial[] materials , // Сохраненные материалы 
             Microsoft.DirectX.Direct3D.EffectInstance effects     // Файл шейдера для поддержки Mesh-объекта 
          )

Параметр options может быть представлен поразрядной комбинацией значений. Из множества перегрузок нужно выбирать такой метод загрузки данных, для которого имеется вся необходимая информация.

Для демонстрации загрузки Mesh -данных из файла создадим форму Form2

  • Закройте все документы текущего редактирования командой оболочки Window/Close All Documents
  • Через панель Solution Explorer скопируйте файл Form1.cs и переименуйте его в файл Form2.cs
  • Откройте файлы Form2.cs (в режиме View Code ) и Form2.designer.cs и замените в них все вхождения Form1 на Form2 с помощью окна замены, вызываемого комбинацией клавиш Ctrl-H (окно замены должно быть настроено в соответствии со снимком: на поиск вхождений в открытых документах)


  • Установите через меню Project/RenderMesh Properties форму Form2 стартовой, постройте приложение и убедитесь, что форма Form2 сохранила функциональность Form1

Теперь пришла пора добавить в форму Form2 код, который будет загружать сложный Mesh -объект из X-файла.

  • Добавьте в класс Form2 объявления полей-ссылок объектов материалов и текстур, которые будут адресовать соответствующие данные, загруженные из X-файла
private Device device = null;
        private Mesh mesh = null;
    
        // Ссылки на массивы для загрузки из X-файла
        private Material[] meshMaterials;
        private Texture[] meshTextures;
    
        public void InitializeGraphics()
        {
            // .....................................................
        }
Листинг 14.16. Добавление в класс Form2 ссылочных переменных
  • Создайте функцию-член класса Form2 для загрузки Mesh -объекта из файла (аргументом функции будет имя файла). Назовите функцию LoadMesh(), она будет возвращать ссылку на загруженный Mesh -объект.
// Функция загрузки Mesh-объекта из файла
        private Mesh LoadMesh(string file)
        {
            ExtendedMaterial[] data; // Создаем ссылку на загружаемые данные
            // Загружаем объект из файла
            Mesh localMesh = Mesh.FromFile(file, MeshFlags.Managed, device, out data);
    
            // Если есть данные - разделяем и сохраняем их
            if ((data != null) && (data.Length > 0))
            {
                // Создаем массив материалов
                meshMaterials = new Material[data.Length];
                // Создаем массив текстур
                meshTextures = new Texture[data.Length];
    
                // Заполняем массив материалами и текстурами
                for (int i = 0; i < data.Length; i++)
                {
                    meshMaterials[i] = data[i].Material3D;
                    if ((data[i].TextureFilename != null)
                        && (data[i].TextureFilename != string.Empty))
                    {
                        // Структура существует, попробуем ее загрузить
                        meshTextures[i] = TextureLoader.FromFile(
                            device, data[i].TextureFilename);
                    }
                }
            }
    
            return localMesh;
        }
Листинг 14.17. Функция загрузки Mesh-объекта из файла
  • Удалите из класса функцию DrawCubes() на ее место вставьте новую функцию рисования объекта DrawMesh()
private void DrawMesh()
        {
            // Вращаем объект
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 2.0f,
                angle / (float)Math.PI / 4.0f) *
                Matrix.Translation(0.0f, 0.0f, 0.0f);
    
            // Загружаем в устройство материал и текстуру поверхности отражения
            for (int i = 0; i < meshMaterials.Length; i++)
            {
                device.Material = meshMaterials[i];
                device.SetTexture(0, meshTextures[i]);
                mesh.DrawSubset(0);
            }
    
            if (flagRotate)
                angle += 0.02F;
        }
Листинг 14.18. Новая функция DrawMesh() рисования Mesh-объекта
  • Измените в функции OnPaint() вызов функции рисования
protected override void OnPaint(PaintEventArgs e)
        {
            // Очистить цветом клиентскую область формы
            device.Clear(ClearFlags.Target,
                System.Drawing.Color.CornflowerBlue,
                1.0F, 0);
    
            // Вызов нашей функции установки камеры
            SetupCamera();
    
            // Сформировать сцену
            device.BeginScene();
            DrawMesh();
            device.EndScene();
    
            // Показать буфер кадра 
            device.Present();
    
            // Принудительно перерисовать
            this.Invalidate();
        }
Листинг 14.19. Вызов DrawMesh() в функции OnPaint()
  • Чтобы отображаемый объект не казался слишком большим, замените в функции SetupCamera() настройки масштаба изображения камеры следующими значениями
// Установка камеры в сцену
        private void SetupCamera()
        {
            //Создание перспективы 
            device.Transform.Projection = Matrix.PerspectiveFovLH(
                (float)Math.PI / 4, // Угол зрения равен 45 градусов
                                    // Форматное соотношение сторон
                (float)this.ClientSize.Width / (float)this.ClientSize.Height, 
                1.0F,               // Ближний план
                1000.0F);           // Дальний план
    
            //Добавление камеры 
            device.Transform.View = Matrix.LookAtLH(
                new Vector3(0, 0, 700.0F),  // Положение камеры
                new Vector3(),              // Положение объекта текущее
                new Vector3(0, 1, 0));      // Направление камеры
    
            // Освещение
            device.RenderState.Lighting = true;
    
            // Установить красный свет
            device.RenderState.Ambient = Color.Red;
    
            // Настроить общий свет
            device.Lights[0].Type = LightType.Directional;
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Direction = new Vector3(0, -1, -1);
            device.Lights[0].Commit();
            device.Lights[0].Enabled = true;
        }
Листинг 14.20. Изменение масштаба изображения камеры в функции SetupCamera()
  • Удалите из функции InitializeGraphics() код генерации куба и вместо него поставьте вызов функции LoadMesh() загрузки Mesh -объекта из файла
public void InitializeGraphics()
        {
            // Создание объекта и настройка параметров представления
            // Создать объект параметров представления
            PresentParameters presentParams = new PresentParameters();
            // Установить оконный режим
            presentParams.Windowed = true;
            // Сбрасывать содержимое буфера, если он не готов к представлению
            presentParams.SwapEffect = SwapEffect.Discard;
    
            // Создать объект устройства и сохранить ссылку на него
            device = new Device(0, DeviceType.Hardware, this,
                CreateFlags.SoftwareVertexProcessing, presentParams);
    
            // Генерация и подключение к устройству куба 
            // с помощью статического метода класса Mesh
            mesh = Mesh.Box(device, 2.0F, 2.0F, 2.0F);
    
            // Загрузить Mesh-объект из файла tiny.x
            mesh = LoadMesh("tiny.x");
        }
Листинг 14.21. Замена генерации куба на функцию LoadMesh() загрузки Mesh-объекта из файла
  • В панели Solution Explorer вызовите контекстное меню для узла проекта и командой Add/Existing Item добавьте к проекту из прилагаемого к работе каталога Source файлы tiny.x и Tiny_skin.bmp, предварительно настроив фильтр окна подкачки

  • В панели Solution Explorer выделите одновременно скопированные оболочкой в проект файлы и щелчкните по пиктограмме Properties, чтобы вызвать для них панель настройки свойств оболочки


  • Настройте панель свойств в соответствии со снимком, чтобы оболочка при компиляции копировала в каталог bin размещения сборки самые свежие версии указанных файлов


  • Запустите приложение и обратите внимание, что некоторые части объекта не закрываются передним планом

Это происходит потому, что мы ранее удалили буфер глубины из параметров устройства. Исправим это:

  • Добавьте буфер глубины в функцию InitializeGraphics() перед созданием устройства
public void InitializeGraphics()
        {
            // Создание объекта и настройка параметров представления
            // Создать объект параметров представления
            PresentParameters presentParams = new PresentParameters();
            // Установить оконный режим
            presentParams.Windowed = true;
            // Сбрасывать содержимое буфера, если он не готов к представлению
            presentParams.SwapEffect = SwapEffect.Discard;
    
            // Настройка буфера глубины для параметров устройства
            presentParams.EnableAutoDepthStencil = true;
            presentParams.AutoDepthStencilFormat =
                Microsoft.DirectX.Direct3D.DepthFormat.D16;
    
            // Создать объект устройства и сохранить ссылку на него
            device = new Device(0, DeviceType.Hardware, this,
                CreateFlags.SoftwareVertexProcessing, presentParams);
    
            // Генерация и подключение к устройству куба 
            // с помощью статического метода класса Mesh
            mesh = Mesh.Box(device, 2.0F, 2.0F, 2.0F);
    
            // Загрузить Mesh-объект из файла tiny.x
            mesh = LoadMesh("tiny.x");
        }
Листинг 14.22. Добавление в функцию InitializeGraphics() буфера глубины
  • Добавьте в функцию OnPaint() флаг очистки устройства с учетом существования буфера глубины
protected override void OnPaint(PaintEventArgs e)
        {
            // Очистить цветом клиентскую область формы
            device.Clear(ClearFlags.Target 
                | ClearFlags.ZBuffer,
                System.Drawing.Color.CornflowerBlue,
                1.0F, 0);
    
            // Вызов нашей функции установки камеры
            SetupCamera();
    
            // Сформировать сцену
            device.BeginScene();
            DrawMesh();
            device.EndScene();
    
            // Показать буфер кадра 
            device.Present();
    
            // Принудительно перерисовать
            this.Invalidate();
        }
Листинг 14.23. Добавление флага существования буфера глубины в функцию OnPaint()
  • Запустите приложение и полюбуйтесь на сложный вращающийся Mesh -объект, который мы загрузили из внешнего X-файла


  • Сдайте работу преподавателю