Архив ‘Graphics’

Silverlight

Четверг, 24 Апрель, 2008

В экспериментальном виде переделываю на работе интерфейс бизнес веб-приложения под Silverlight. Получается красиво.

Silverlight практически не требует обучения: все достаточно прозрачно и понятно. Если кого-то заинтересует — опишу в блоге процесс подробнее.

Рендер внутри контрола, внутри окна

Пятница, 14 Декабрь, 2007

Из рубрики “Нам пишут”. Вопрос поставлен так: “Можно ли в managed d3d рендерить внутри контрола, который расположен внутри окна?”. Я и первый раз неплохо понял: контрол он, как бы, без окна никак. Можно.

Объяснять как это делается, равносильно созданию очередного шедевра типа — “Managed Direct3D For Complete Idiots”. Правда есть небольшой оправдательный момент — подводный камень обнаружен в разделе Program.cs.

Для начала я создал проект-болванчик: Visual C# - Windows - Windows Application. Затем добавил свежий GridMesh.cs. Как таковой он для проекта не нужен, но мне нужно что-то отрисовать в окне.

Быстро накидав контролов внутрь окна (для имитации видимости), я разбавил код Form1.cs методами необходимыми для поддержки Direct3D. Изменения коснулись только одного места:

Form1.cs

public bool InitializeGraphics()
{
   
try
    {
       presentParams.Windowed =
true;
       presentParams.SwapEffect =
SwapEffect.Discard;
       presentParams.AutoDepthStencilFormat =
DepthFormat.D16;
       presentParams.EnableAutoDepthStencil =
true;
       
       d3dDevice =
           
new Microsoft.DirectX.Direct3D.Device(
               0,
               Microsoft.DirectX.Direct3D.
DeviceType.Hardware,

               splitContainer1.Panel2,

               CreateFlags.HardwareVertexProcessing,
               presentParams);
       d3dDevice.DeviceReset +=
new EventHandler(this.OnDeviceReset);
       d3dDevice.DeviceLost +=
new EventHandler(this.OnDeviceLost);

       OnCreateDevice();

       return true;
   }
   
catch (DirectXException)
   {
       
return false;
   }
}

Осталось подвинуть “подводный камень” и можно запускать:

Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace RenderToControl
{
   
static class Program
    {
       
/// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
       
static void Main()
       {
           
Application.EnableVisualStyles();
           
Application.SetCompatibleTextRenderingDefault(false);

           using (Form1 mainForm = new Form1())
           {
               
if (!mainForm.InitializeGraphics())
               {
                   
MessageBox.Show(“Could not initialize Direct3D.”);
                   
return;
               }
               mainForm.Show();

               while (mainForm.Created)
               {
                   mainForm.Render();
                   
Application.DoEvents();
               }
           }
       }
   }
}

Запуск! Рендер внутри контрола, внутри окна работает.

render2control.png

По теме
- GridMesh Helper в Managed Direct3D

GridMesh Helper в Managed Direct3D

Пятница, 14 Декабрь, 2007

Когда надоело ковыряться в чужих исходниках игрушек, я снова переключился на Managed Direct3D. Для небольшой подготовительной работы мне понадобилась прямоугольная плоскость.

Просто взять и нарисовать прямоугольник не есть хорошо: нужно разбить его на ряд треугольников (лучше, не меньше двухсот). Да и смотреться такая плоскость будет красивее, при условии, что будет включен режим отрисовки device.RenderState.FillMode = FillMode.WireFrame; (иначе говоря, рисуем сетку без заливки).

Чтобы проделанная работа не пропадала зря, я оформил сетку в отдельный класс хелпер, чем несказанно рад и доволен.

gridmeshhelper.png

В коде присутствуют индексы и некая оптимизация (взято из MSDN), а также ряд конструкторов, используя которые совсем расслабляешься. Что, впрочем, и хотелось сделать.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX.Direct3D;
using System.Drawing;
using Microsoft.DirectX;

namespace Helpers
{
   
class GridMesh
    {
        #region Constructors
       
public GridMesh(Device graphicsDevice)
       {
           rows = 1;
           columns = 1;
           cellW = 1.0f;
           cellH = 1.0f;
           device = graphicsDevice;

           Update();
       }

       public GridMesh(Device graphicsDevice, float gridWidth, float gridHeight, int gridRows, int gridColumns)
       {
           rows = gridRows;
           columns = gridColumns;
           cellW = gridWidth / rows;
           cellH = gridHeight / columns;
           device = graphicsDevice;

           Update();
       }

       public GridMesh(Device graphicsDevice, float gridWidth, float gridHeight)
       {
           rows = 10;
           columns = 10;
           cellW = gridWidth / columns;
           cellH = gridHeight / rows;
           device = graphicsDevice;

           Update();
       }

       public GridMesh(Device graphicsDevice, int gridRows, int gridColumns, float cellWidth, float cellHeight)
       {
           rows = gridRows;
           columns = gridColumns;
           cellW = cellWidth;
           cellH = cellHeight;
           device = graphicsDevice;

           Update();
       }
        #endregion

        protected void Update()
       {
           vertexes =
new CustomVertex.PositionOnly[(rows + 1) * (columns + 1)];
           
for (int z = 0; z <= rows; z++)
           {
               
for (int x = 0; x <= columns; x++)
               {
                   vertexes[x + z * (columns + 1)] =
                       
new CustomVertex.PositionOnly(
                           x * cellW,
                           0.0f,
                           z * cellH);
               }
           }

           indexes = new short[VertexCount];
           
int idx = 0;
           
for (int z = 0; z < rows; z++)
           {
               
for (int x = 0; x < columns; x++)
               {
                   
int vIdx = x + z * columns + z;
                   
int vIdxN = vIdx + 1 + columns + 1;
                   indexes[idx++] = (
short)(vIdx);
                   indexes[idx++] = (
short)(vIdx + 1);
                   indexes[idx++] = (
short)(vIdxN);

                   indexes[idx++] = (short)(vIdx);
                   indexes[idx++] = (
short)(vIdxN);
                   indexes[idx++] = (
short)(vIdx + 1 + columns);
               }
           }

           mesh = new Mesh(FaceCount, VertexCount, 0, VertexFormats.Position, device);
           mesh.SetVertexBufferData(vertexes,
LockFlags.None);
           mesh.SetIndexBufferData(indexes,
LockFlags.None);

           // optimization
            int[] adjacency = new int[mesh.NumberFaces * 3];
           mesh.GenerateAdjacency(0.01F, adjacency);
           mesh.OptimizeInPlace(
MeshFlags.OptimizeVertexCache, adjacency);
       }

       public void Render()
       {
           mesh.DrawSubset(0);
       }
       
       
int rows;
       
int columns;
       
float cellW;
       
float cellH;
       
Device device;

       CustomVertex.PositionOnly[] vertexes;
       
short[] indexes;
       
Mesh mesh;

        #region Getters and Setters
       
public int Rows
       {
           
set
            {
               rows =
value;
               Update();
           }
       }

       public int Columns
       {
           
set
            {
               columns =
value;
               Update();
           }
       }

       public int CellWidth
       {
           
set
            {
               cellW =
value;
               Update();
           }
       }

       public int CellHeight
       {
           
set
            {
               cellH =
value;
               Update();
           }
       }

       public Mesh Mesh
       {
           
get { return mesh; }
       }
       
       
public CustomVertex.PositionOnly[] Vertexes
       {
           
get { return vertexes; }
       }

       public short[] Indexes
       {
           
get { return indexes; }
       }

       public int VertexCount
       {
           
get { return rows * columns * 6; }
       }

       public int FaceCount
       {
           
get { return rows * columns * 2; }
       }

       public int CellCount
       {
           
get { return rows * columns; }
       }

       public float Width
       {
           
get { return columns * cellW; }
       }

       public float Height
       {
           
get { return rows * cellH; }
       }
        #endregion
    }
}

Серия статей о Managed Direct3D

Понедельник, 19 Ноябрь, 2007

Серия англоязычных статей, понравившихся мне своим стилем изложения, можно найти здесь.

* * *

Q: Насколько Managed Direct 3D медленнее unmanaged D3D (читай — может лучше писать на C++, чем на C#?)

Приблизительно на 80% от максимальной производительности. Все-таки не стоит забывать о сборщике мусора и врапперах вокруг СОМ-объектов Direct3D.

Managed D3D медленнее, но не настолько, чтобы жертвовать удобством разработки в сторону C++. Выбор за вами.

* * *

В небольшой битве за скорость вывода графики под managed c# выиграл Managed Direct3D. XNA — для игрушек, да и не ладится с ним как-то. GDI+ тормозит, а врапперствовать над Win GDI — сродни некрофилии.

С Direct3D я получаю всю мощь современной видеосистемы. Managed C# дает ООП и классы хелперы. Раза в два усложняется инициализация и поддержка графики в приложении, но скорость выполнения — это нечто!

Microsoft прочит большое будущее .net платформе, а мы посмотрим.

Болванчик для XNA

Среда, 24 Октябрь, 2007

Почему XNA, а не OpenGL или DirectX? Чтобы проникнуться, рекомендую изучить вебкаст от Microsoft: Visual C# Soup to Nuts. Part 21: Visual C# and Game Development.

Для начала нужно убедиться, что драйвер видеокарты поддерживает DX 9.0, а сама видеокарта поддерживает пиксельные шейдеры версии 1.1 или более. GeForce 4 MX не пойдет, проверено. Если не выполнить эти условия, проявляется исключение (exception): “Could not find a Direct3D device that has a Direct3D9-level driver and supports pixel shader 1.1 or greater.”

Итак, установка XNA. Затем, последний DirectX. Если не установить один из последних DX’ов, то легко «ловится» странное исключение: “Exception from HRESULT: 0×8007007E”.

Нас тройка

Пора кодить, но сначала настройка проекта. Открываю «студию» (C# Express вполне подходит) и создаю пустой проект. Называю «DummyGame».

01newemptyproject.png

В «Solution Explorer’е» выбираю Properties проекта.

02properties.png

Меняю «Output Type» на «Windows Application».

03propwinapp.png

Основа проекта готова. Добавляю ссылки (references) на сборки XNA.

04addrefs.png

Всего понадобятся две сборки:
- Microsoft.Xna.Framework.dll
- Microsoft.Xna.Framework.Game.dll

05addassemblies.png

На этом этапе есть большой подводный камень. Если XNA framework «встал» удачно, то ссылки спокойно добавляются с помощью вкладки «.NET», если же сборки не находятся, то придется немного сплясать с бубном. Следующее описание пропускается, если сборки благополучно найдены в GAC’е.

Для начала, создаю в проекте каталог «lib».

06addlib.png

Теперь иду в каталог:

…\WINDOWS\assembly\GAC_32\Microsoft.Xna.Framework\1.0.0.0__xxx\

и нахожу там: Microsoft.Xna.Framework.dll. Копирую его в подготовленный каталог проекта «lib».

Иду в каталог:

…\WINDOWS\assembly\GAC_MSIL\Microsoft.Xna.Framework.Game\1.0.0.0__xxx\

и повторяю действие описанное выше, но уже с Microsoft.Xna.Framework.Game.dll.

Теперь возвращаюсь к пункту с назначением ссылок и проделываю это через вкладку «Browse».

У меня получилось нечто подобное скриншоту ниже:

07refresult.png

Ближе к коду

Создаю класс DummyGame.cs.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DummyGame
{
   
class DummyGame : Microsoft.Xna.Framework.Game
    {
       
const int ScreenWidth = 800;
       
const int ScreenHeight = 600;

       GraphicsDeviceManager graphics;

       public DummyGame()
       {
           graphics =
new GraphicsDeviceManager(this);
           graphics.PreferredBackBufferWidth = ScreenWidth;
           graphics.PreferredBackBufferHeight = ScreenHeight;
       }

       /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input and playing audio.
        /// </summary>
        /// <param name=”gameTime”>Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
       {
           
// Allows the default game to exit on Xbox 360 and Windows
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
               
this.Exit();

           base.Update(gameTime);
       }

       /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name=”gameTime”>Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
       {
           
// Clear frame-buffer:
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

           base.Draw(gameTime);
       }
   }
}

Создаю класс Program.cs.

using System;

namespace DummyGame
{
   
static class Program
    {
       
/// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
       {
           
DummyGame theGame = new DummyGame();
           theGame.Run();
       }
   }
}

Готово. F5!

f5result.png