воскресенье, 11 ноября 2012 г.

Распаковка DxtCompressed текстур


Статья актуальна для разработки в XNA Game Studio и MonoGame.

MSDN Library: Opaque and 1-Bit Alpha Textures (Direct3D 9)

В данной статье приведу пример распаковки DxtCompressed текстуры. DxtCompressed текстуры имеют несколько форматов сжатия. Ниже приведенный пример распаковки предназначен для работы с форматом DXT1. В моих проектах это наиболее распространенный применяемый формат. В DXT1 прозрачность кодируется только двумя значениями 0 и 1 т.е. ни какой полу прозрачности, что дает преимущество сжатия в 4 раза. Для сравнения в DXT5 поддерживает полу прозрачность, которая кодируется отдельным блоком, за счет чего текстура жмется всего в 2 раза.

Для чего может понадобиться распаковка текстуры? Например, что бы по признаку прозрачности построить области проходимости игрового уровня, а так же многое другое.

Код примера можно разбить на части:
- загрузка DxtCompressed текстуры;
- распаковка и преобразование в массив цветов Color[];
- преобразование массива цветов в обычную текстуру;
- вывод текстуры на экран для визуальной проверки.

Для работы примера необходимо:
- создать новый проект Windows Game (4.0);
- в Content добавить нашу DXT1 текстуру;
- в свойствах этой текстуры переключить параметр "Texture Format" с "Color" в "DxtCompressed"


- добавляем код из примера.

Код:  

 
 using System;
 
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Input;
 
 namespace DxtCompressed
 {
     public class Game1 : Microsoft.Xna.Framework.Game
     {
         GraphicsDeviceManager _graphics;
         SpriteBatch _spriteBatch;
 
         Texture2D _dxtCompressedTexture;
         Texture2D _colorTexture;
 
         public Game1()
         {
             _graphics = new GraphicsDeviceManager(this);
             Content.RootDirectory = "Content";
         }
 
         protected override void LoadContent()
         {
             _spriteBatch = new SpriteBatch(GraphicsDevice);
             _dxtCompressedTexture = Content.Load<Texture2D>("512x256");
             _colorTexture = Unpacking(_dxtCompressedTexture);
         }
 
         protected override void Update(GameTime gameTime)
         {
             if(Keyboard.GetState().IsKeyDown(Keys.Escape))
                 Exit();
         }
 
         protected override void Draw(GameTime gameTime)
         {
             GraphicsDevice.Clear(Color.CornflowerBlue);
             _spriteBatch.Begin();
             _spriteBatch.Draw(_colorTexture, Vector2.Zero, Color.White);
             _spriteBatch.End();
         }
 
         public struct DXT1pixel
         {
             public ushort color0;
             public ushort color1;
 
             public byte[,] pixel;
 
             public DXT1pixel(byte[] b)
             {
                 color0 = 0;
                 color0 |= (ushort)(b[1] << 8);
                 color0 |= (ushort)b[0];
                 color1 = 0;
                 color1 |= (ushort)(b[3] << 8);
                 color1 |= (ushort)b[2];
                 pixel = new byte[4, 4];
                 byte[] bbbb;
                 for(int i = 0; i < 4; i++)
                 {
                     bbbb = DecompressByte(b[i + 4]);
                     pixel[0, i] = bbbb[0];
                     pixel[1, i] = bbbb[1];
                     pixel[2, i] = bbbb[2];
                     pixel[3, i] = bbbb[3];
                 }
             }
 
             private byte[] DecompressByte(byte b)
             {
                 byte[] bb = new byte[4];
                 for(int i = 0; i < 4; i++)
                 {
                     bb[i] = (byte)(b & 0x03);
                     b = (byte)(b >> 2);
                 }
                 return bb;
             }
         }
 
         private Color WordToColor(int word)
         {
             int r,g,b;
             b = word & 0x1f;
             word = word >> 5;
             g = word & 0x3f;
             word = word >> 6;
             r = word;
             return new Color(r * 8, g * 4, b * 8);
         }
 
         public Texture2D Unpacking(Texture2D dxtTexture)
         {
             if(dxtTexture.Format != SurfaceFormat.Dxt1)
                 return null;
             int _width = dxtTexture.Width;
             int _height = dxtTexture.Height;
             int w = _width >> 2;
             int h = _height >> 2;
             byte[] color = new byte[w * h * 8];
             dxtTexture.GetData<byte>(color);
             //
             DXT1pixel[,] dxt = new DXT1pixel[w, h];
             int i;
             byte[] b = new byte[8];
             for(int y = 0; y < h; y++)
             {
                 for(int x = 0; x < w; x++)
                 {
                     i = (y * w + x) * 8;
                     Array.Copy(color, i, b, 0, 8);
                     dxt[x, y] = new DXT1pixel(b);
                 }
             }
             i = 0;
             Color[] newColor = new Color[_width * _height];
             for(int y = 0; y < h; y++)
             {
                 for(int x = 0; x < w; x++)
                 {
                     for(int yy = 0; yy < 4; yy++)
                     {
                         for(int xx = 0; xx < 4; xx++)
                         {
                             int ic = (y * 4 + yy) * _width + x * 4 + xx;
                             Color color0 = WordToColor(dxt[x, y].color0);
                             Color color1 = WordToColor(dxt[x, y].color1);
                             if(dxt[x, y].color0 > dxt[x, y].color1) 
                             {
                                 Color color2 = new Color(
                                     (int)(color0.R * 0.66f + color1.R * 0.34f),
                                     (int)(color0.G * 0.66f + color1.G * 0.34f),
                                     (int)(color0.B * 0.66f + color1.B * 0.34f));
                                 Color color3 = new Color(
                                     (int)(color0.R * 0.34f + color1.R * 0.66f),
                                     (int)(color0.G * 0.34f + color1.G * 0.66f),
                                     (int)(color0.B * 0.34f + color1.B * 0.66f));
                                 switch(dxt[x, y].pixel[xx, yy])
                                 {
                                     case 0x00:
                                         newColor[ic] = color0;
                                         break;
                                     case 0x01:
                                         newColor[ic] = color1;
                                         break;
                                     case 0x02:
                                         newColor[ic] = color2;
                                         break;
                                     case 0x03:
                                         newColor[ic] = color3;
                                         break;
                                 }
                             }
                             else 
                             {
                                 
                                 Color color2 = new Color(
                                     (int)(color0.R / 2 + color1.R / 2),
                                     (int)(color0.G / 2 + color1.G / 2),
                                     (int)(color0.B / 2 + color1.B / 2));
                                 Color color3 = Color.Transparent;
                                 
                                 switch(dxt[x, y].pixel[xx, yy])
                                 {
                                     case 0x00:
                                         newColor[ic] = color0;
                                         break;
                                     case 0x01:
                                         newColor[ic] = color1;
                                         break;
                                     case 0x02:
                                         newColor[ic] = color2;
                                         break;
                                     case 0x03:
                                         newColor[ic] = color3;
                                         break;
                                 }
                             }
                             
                         }
                     }
                 }
             }
             Texture2D t = new Texture2D(_graphics.GraphicsDevice, _width, _height);
             t.SetData<Color>(newColor);
             return t;
         }
     }
 }
 
 
 
 
 
 

Как работать с XNA Game Studio в Visual Studio 2012?

Александр Сороколетов подсказал волшебную статью:
How to install XNA game studio on Visual Studio 2012?

суббота, 15 октября 2011 г.

"GB&W Lite" [wp7][game][release]

Выпустил лайт версию игры GB&W.



Вот видео с мастер классом по прохождению 19го уровня:



игру можно найти тут: 


Как видите есть ряд изменений в оформлении, они коснулись и полной версии приложения для которого вышло соответствующее обновление.

Оказалось, при первой публикации лайт версии, GB&W занимает в памяти более 90 МБ (на сколько не скажу). Очень много анимированной растровой рафики. Лайт версию пришлось немного порезать. Получается для GB&W мне в свое время сделали скидку :).

среда, 13 июля 2011 г.

Компромисс в выборе между классом и структурой

На тему навела вот эта статья "NET 4.0: Class vs Struct или в чём различия между Классом и Структурой" . Давно уже собирался собрать для себя набор иерархии универсальных коллекций, которые не попадают под сборку мусора. Тема в очередной раз стала интересна после моего известного эксперимента с Windows Phone 7. Очень сомневаюсь, что моя статья будет полезна для бизнес-приложений.  Меня больше интересует данная тема для использования в XNA/DirectX и как область применения - игры.

Приглашаю вас обсудить данный вопрос.

И так, условия задачи:
- Нужна коллекция элементов для описания, например, системы частиц (это может быть все что угодно). Такая штука в которой в среднем 60 раз в секунду добавляются и удаляются сотни элементов.
- Изменения в коллекции не должны влиять на сборку мусора.
- Нельзя использовать структуры! Только ссылочные типы.
- Запрещено создавать и удалять классы, за исключением первой инициализации.
- Физическое количество элементов в коллекции должно быть постоянным. Т.к. изменение длинны массива достаточно накладная операция, а количество элементов должно постоянно плавать, то подойдет подобие кеша. Ограничение - разработчику желательно четко знать максимально возможное количество элементов в такой коллекции. А это значит игра должна быть грамотно спроектирована. Хотя лазейку я оставлю.


Набросал вот такой черновик.

Базовый класс для элемента коллекции:

public class TElementConstLength
    {
        private  int indexInCollection = -1;
        internal int IndexInCollection { get { return indexInCollection; } }
        internal void SetIndexInCollection(int i)
        {
            indexInCollection = i;
        }
    }


Базовый класс для коллекции:

public class TCollectionConstLength<T> where T : TElementConstLength, new()
    {
        private T[] elements;

        private int count = 0;
        /// <summary>
        /// Количество живых элементов
        /// </summary>
        public  int Count { get { return count; } }

        private int end = -1;

        /// <summary>
        /// Инициализация колекции
        /// </summary>
        /// <param name="length">Длинна коллекции</param>
        public TCollectionConstLength(int length)
        {
            elements = new T[length];
        }

        /// <summary>
        /// Доступ к элементу по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        /// <returns>Элемент</returns>
        public T this[int index]
        {
            get { return elements[index]; }
        }

        /// <summary>
        /// Выделение места под новый элемент
        /// </summary>
        /// <returns>Новый элемент</returns>
        public virtual T GetNew() // = Add
        {
            // если список полон, для режима отладки
            if(end == elements.Length - 1)
                Array.Resize(ref elements, elements.Length + 1);
            // 
            end++;
            count++;
            // если не инициализирован элемент
            if(elements[end] == null)
            {
                elements[end] = new T();
                elements[end].SetIndexInCollection(end);
            }
            //
            return elements[end];
        }

        /// <summary>
        /// Очистка списка
        /// </summary>
        public void Clear()
        {
            end = -1;
            count = 0;
        }

        /// <summary>
        /// Удаление элемента по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        public virtual void Remove(int index)
        {
            // выход за пределы
            if(index < 0 || index > end)
                return;
            if(index < end)
            {
                // рокировка элементов
                T temp = elements[end];
                elements[end] = elements[index];
                elements[index] = temp;
                // рокировка индексов
                int i = elements[end].IndexInCollection;
                elements[end].SetIndexInCollection(elements[index].IndexInCollection);
                elements[index].SetIndexInCollection(i);
            }
            // уменьшаем список
            end--;
            count--;
        }

        /// <summary>
        /// Удаление элемента
        /// </summary>
        /// <param name="e">Элемент</param>
        public virtual void Remove(T e)
        {
            // проверка на соответствие ссылок (защита от тупости)
            if(e != elements[e.IndexInCollection])
                return;
            Remove(e.IndexInCollection);
        }
    }

Вот пример для тестов:

using System;

namespace CollectionConstLength
{

    public class ElementTest : TElementConstLength
    {
        private string name;
        public string Name { get { return name; } set { name = value; } }
        public ElementTest() { }
    }

    class Program
    {

        static TCollectionConstLength<ElementTest> item;

        static void Main(string[] args)
        {

            item = new TCollectionConstLength<ElementTest>(5);
            ElementTest et;
            for(int i = 0; i < 5; i++)
            {
                et = item.GetNew();
                et.Name = i.ToString();
            }
            //
            Print();
            //
            item.Remove(item[1]);
            Print();
            //
            Console.ReadKey();
        }

        static void Print()
        {
            Console.WriteLine("//--------------------------------");
            for(int i = 0; i < item.Count; i++)
            {
                Console.WriteLine(
                    string.Format("index - {0}; name - \"{1}\";", 
                        item[i].IndexInCollection, 
                        item[i].Name));
            }
            Console.WriteLine("//--------------------------------");
        }
    }
}


Пример наращивания функционала через наследование:

- Расширенный класс элемента:

public class TUpdate : TElementConstLength
    {
        public virtual void Update() { }
    }

- Расширенный класс коллекции

public class TUpdateCollection<T> : TCollectionConstLength<T> where T : TUpdate, new()
    {
        public TUpdateCollection(int l) : base(l) { }
        public virtual void Update()
        {
            for(int i = 0; i < base.Count; i++)
            {
                base[i].Update();
            }
        }
    }


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

Давайте пообщаемся на эту тему.
Так же буду рад ссылкам на статьи с подобными темами и изысканиями.

суббота, 2 июля 2011 г.

Лето ... Юмор ...


Бред конечно, но когда пытаешься сосредоточиться на работе, а тебе при этом постоянно мешают, еще эта жара - именно такие мысли лезут в голову:

Тенгизи: к сожалению, пока некогда :(( работа... осенью закуплю девайсы и морозными зимними вечерами... :)
Dmitry: да.. да..)))
Dmitry: я вот тут думаю, как бы до морозных вечеров обороты набрать, что бы потом только щепки летели)))
Тенгизи: лето... мозги вообще ничего делать не хотят... :((
Тенгизи: у нас жара вторую неделю... жесть...
Dmitry: ищу стимулы, у нас тоже пекло)
Тенгизи: ну у вас-то еще бы!
Dmitry: )))
Тенгизи: мы-то люди северные, к жаре не привычные
Dmitry: да я лето из за лени ненавижу)))
Тенгизи: хотя я 17 лет в минводах прожил, но тут уже адаптировался... )
Dmitry: у меня такое подозрение, что для того что бы я смог раскрыть все свои способности, по максимуму, и реализовать их - меня нужно запереть на какой ни будь полярной станции и не выпускать.
Dmitry: только с компом и интернетом)))
Dmitry: и раз в неделю привозить еду и женщин)))))))))))
Dmitry: ух ...
Тенгизи: у меня такое же мнение!!! сто пудово! если запереть - реализуемся...
Тенгизи: даже женщин на хрен не возите, только отвлекать будут... потерплю тройку месяцев ))))
Dmitry: раз в неделю можно часок расслабиться)))
Тенгизи: при таком раскладе даже в покер научится играть можно профессионально... ))
Тенгизи: ну.. если часок... но потом чтоб СРАЗУ УВОЗИЛИ! :))
Dmitry: прилетела на вертолете, еды привезла, удовлетворила и улетела)))
Dmitry: идеал женщины)))
Тенгизи: )))))))
Тенгизи: к тому же чтоб глухонемая была ))
Dmitry: не вопрос)))

Приятно осознавать, что ты не один такой :) 

"GB&W" in "Top WP7 Games : June 2011 [ POLL ]"

Сайт bestwp7games.com предложил голосование за игры для Windows Phone 7 вышедшие в июне. В этом списке на ряду с топовыми играми, имеющими статут xbox live, каким то образом, оказалась и наша игра GB&W. Приглашаю вас присоединиться к нашей группе болельщиков и принять участие в голосовании.

Проголосовать можно тут

Регистрация не нужна. Достаточно выбрать соответствующий пункт и подтвердить кнопкой "vote".

Всем огромное спасибо за отклик и участие!

P.S. Топовым играм, имеющим статус xbox live и команду поддержки, хватает внимания, загрузок и покупок, а вот нам бы все это даже очень не помешало.