netlib.narod.ru< Назад | Оглавление | Далее >

Скелетная анимация в XNA

В XNA есть хорошо документированный конвейер содержимого, разделенный на различные слои и предоставляющий импортеры, обработчики, компиляторы (записыватели содержимого) и считыватели (читатели содержимого) для игровых ресурсов. Поскольку в конвейере содержимого XNA нет полной поддержки для моделей со скелетной анимацией, вам необходимо расширить конвейер содержимого, добавив поддержку для скелетной анимации. Заметьте, что конвейер содержимого частично поддерживает скелетную анимацию, поскольку может импортировать данные скелетной анимации из файлов X и FBX, но не в состоянии обработать все импортированные данные скелетной анимации. На рис. 11.4 показана упрощенная диаграмма классов конвейера содержимого, применяемых для импорта, обработки, компиляции и чтения файлов моделей.


Рис. 11.4. Конвейер содержимого XNA - классы, используемые для импорта, обработки, компиляции и чтения игровых моделей

Рис. 11.4. Конвейер содержимого XNA — классы, используемые для импорта, обработки, компиляции и чтения игровых моделей


Сперва модель импортируется соответствующим ей импортером содержимого и каждый импортер содержимого конвертирует исходные данные модели в формат объектной модели документа (DOM) XNA. Благодаря этому после импортирования моделей все они будут в одном и том же формате и могут быть обработаны соответствующим им обработчиком содержимого, ModelProcessor. Выходными данными импортера моделей является корневой объект NodeContent, описывающий графический тип, имеющий свою собственную систему координат и могущий иметь потомков. Два класса расширяют класс NodeContent: MeshContent и BoneContent. Итак, корневой объект NodeContent, полученный в результате импорта модели, может иметь несколько потомков NodeContent, MeshContent и BoneContent.

ModelProcessor получает в качестве параметра корневой объект NodeContent, сформированный импортером модели, и возвращает объект ModelContent. Объект ModelContent, сформированный ModelProcessor, содержит обработанные данные модели, которые необходимо сохранить в двоичном файле XNB. Чтобы объект ModelContent можно было сохранить в файл XNB, ModelContent и каждый объект внутри него должны иметь свой собственный ContentTypeWriter. ContentTypeWriter определяет, как данные каждого объекта записываются в файл XNB. И, наконец, во время исполнения ContentManager использует ContentTypeReader для каждого объекта, чтобы прочитать его данные из двоичного файла XNB и вернуть объект Model.

Чтобы добавить в XNA поддержку скелетной анимации, вам надо расширить используемый по умолчанию обработчик моделей, создав новую версию, способную обрабатывать и сохранять скелет модели и анимацию. Помимо этого вам надо создать несколько классов для хранения данных скелетной анимации (скелета модели и анимации), а также классы ContentTypeWriter и ContentTypeReader для записи и чтения этих данных.

На рис 11.5 показаны классы, которые вам надо создать, чтобы расширить конвейер содержимого, добавив поддержку моделей со скелетной анимацией. Классы, которые необходимо создать, отмечены на рис. 11.5 красным.


Рис. 11.5. Расширение показанного на рис. 11.4 конвейера содержимого, которое поддерживает модели со скелетной анимацией

Рис. 11.5. Расширение показанного на рис. 11.4 конвейера содержимого, которое поддерживает модели со скелетной анимацией


Вы создаете классы, используемые для хранения данных скелетной анимации, в отдельной библиотеке, поскольку они будут использоваться обработчиком анимированных моделей для хранения данных скелетной анимации и игровым приложением для загрузки этих данных во время выполнения. Для хранения классов скелетной анимации создайте новый проект Windows Game Library с именем AnimationModelContentWin. Обработчик модели будет использовать классы из этой библиотеки на платформе Windows для хранения данных скелетной анимации. Если ваша игра предназначена для платформы Windows, эту библиотеку можно также использовать для загрузки данных скелетной анимации во время выполнения.

Если вы ведете разработку для Xbox 360, вам надо создать еще один проект: Xbox 360 Game Library с именем AnimationModelContentXbox. Эта библиотека содержит те же самые файлы, что и библиотека AnimationModelContentWin, но приложения Xbox 360 используют ее для загрузки скелетной анимации во время выполнения. Вам все равно нужен проект AnimationModelContentWin, даже если вы ведете разработку только для платформы Xbox 360, поскольку оригинальные файлы моделей импортируются и обрабатываются на платформе Windows, что делает библиотеку Windows необходимой для хранения данных модели.

Для хранения данных скелетной анимации вы создаете три различных класса: Keyframe, AnimationData и AnimatedModelData. Класс Keyframe хранит кадр скелетной анимации, где каждый кадр содержит новую конфигурацию кости скелета. Класс AnimationData хранит массив ключевых кадров, образующих законченную анимацию (такую как бег, прыжок и т.д.). И, наконец, класс AnimatedModelData хранит скелет модели (кости и их иерархию) и массив объектов AnimationData, содержащий все анимации модели.

Класс Keyframe

Класс Keyframe отвечает за хранение кадра анимации кости скелета. В кадре анимации должна быть ссылка на анимируемую кость, новая конфигурация (местоположение и ориентация) указанной кости, и время, в которое эта новая конфигурация должна быть применена. Заметьте, что вы используете ключевые кадры для модификации оригинальной конфигурации кости, заменяя ее текущую конфигурацию на новую. Вы храните конфигурацию кости в виде матрицы, применяя для этого класс XNA Matrix, а время анимации (время, в которое должен быть применен ключевой кадр) хранится как TimeSpan.

В классе AnimatedModelData вы храните скелет модели в виде массива костей, конструируемого путем обхода в глубину скелета модели. Итак, вы можете хранить ссылку на кость, которая должна быть анимирована, в виде целого числа, представляющего индекс кости в массиве bones класса AnimatedModelData. Вот код класса KeyFrame:

public class Keyframe : IComparable
{
    int boneIndex;
    TimeSpan time;
    Matrix transform;

    // Свойства...
    public TimeSpan Time
    {
        get { return time; }
        set { time = value; }
    }

    public int Bone
    {
        get { return boneIndex; }
        set { boneIndex = value; }
    }

    public Matrix Transform
    {
        get { return transform; }
        set { transform = value; }
    }

    public Keyframe(TimeSpan time, int boneIndex, Matrix transform)
    {
        this.time = time;
        this.boneIndex = boneIndex;
        this.transform = transform;
    }

    public int CompareTo(object obj)
    {
        Keyframe keyframe = obj as Keyframe;
        if (obj == null)
            throw new ArgumentException("Object is not a Keyframe.");

        return time.CompareTo(keyframe.Time);
    }
}

В классе Keyframe вы реализуете интерфейс IComparable, чтобы иметь возможность сравнивать объекты Keyframe. Объекты Keyframe сравниваются на основании их времени — атрибута time. Вы используете это сравнение в дальнейшем для сортировки ключевых кадров согласно их времени кадра.

Класс AnimationData

Класс AnimationData отвечает за хранение законченной анимации модели (такой, как бег, прыжок и т.д.). Вы храните каждую анимацию как массив объектов Keyframe, а помимо этих ключевых кадров вы также сохраняете другие полезные данные, такие как имя анимации и ее продолжительность. Вот код класса AnimationData:

public class AnimationData
{
    string name;
    TimeSpan duration;
    Keyframe[] keyframes;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public TimeSpan Duration
    {
        get { return duration; }
        set { duration = value; }
    }

    public Keyframe[] Keyframes
    {
        get { return keyframes; }
        set { keyframes = value; }
    }

    public AnimationData(string name, TimeSpan duration,
                         Keyframe[] keyframes)
    {
        this.name = name;
        this.duration = duration;
        this.keyframes = keyframes;
    }
}

Класс AnimatedModelData

Класс AnimatedModelData отвечает за хранение скелета модели и анимаций. Вы храните скелет модели как массив костей, где каждая кость представлена как матрица. Вы конструируете массив костей путем обхода в глубину скелета модели. Обход в глубину начинается с корневой кости скелета и идет до самой глубокой кости. Когда найдена самая глубокая кость в пути, обход возвращается назад и пытается найти другой возможный путь, по которому снова идет до самой глубокой кости. Например, обход в глубину иерархии на рис. 11.6 возвращает следующий массив: корневая кость, шея, левое плечо, левое запястье, левая ладонь, левая конечная кость, правое плечо, правое запястье, правая ладонь, правая конечная кость.


Рис. 11.6. Пример иерархии скелета

Рис. 11.6. Пример иерархии скелета


Вы храните кости скелета в их конфигурации позиции привязки (bind pose). Позиция привязки — это позиция, в которой кости были связаны с сеткой модели, и она является стартовой позицией любой анимации. Когда модель не анимируется и в самом начале анимации все кости модели находятся в позиции привязки.

В классе AnimatedModelData вы должны создать два атрибута, являющихся массивами значений XNA Matrix для хранения костей скелета, один атрибут являющийся массивом значений int для хранения иерархии костей скелета и один атрибут, являющийся массивом значений AnimationData для хранения анимаций модели. Вот код класса AnimatedModelData:

public class AnimatedModelData
{
    Matrix[] bonesBindPose;
    Matrix[] bonesInverseBindPose;
    int[] bonesParent;
    AnimationData[] animations;

    // Свойства ...
    public int[] BonesParent
    {
        get { return bonesParent; }
        set { bonesParent = value; }
    }

    public Matrix[] BonesBindPose
    {
        get { return bonesBindPose; }
        set { bonesBindPose = value; }
    }

    public Matrix[] BonesInverseBindPose
    {
        get { return bonesInverseBindPose; }
        set { bonesInverseBindPose = value; }
    }

    public AnimationData[] Animations
    {
        get { return animations; }
        set { animations = value; }
    }

    public AnimatedModelData(Matrix[] bonesBindPose,
              Matrix[] bonesInverseBindPose, int[] bonesParent,
              AnimationData[] animations)
    {
        this.bonesParent = bonesParent;
        this.bonesBindPose = bonesBindPose;
        this.bonesInverseBindPose = bonesInverseBindPose;
        this.animations = animations;
    }
}

В классе AnimatedModelData атрибут bonesBindPose хранит массив, содержащий локальную конфигурацию (относительно предка) каждой кости скелета в ее позиции привязки. Атрибут bonesInverseBindPose хранит массив содержащий инвертированную абсолютную конфигурацию (не связанную с предком) каждой кости скелета в ее позиции привязки, а атрибут bonesParent хранит индекс родителя каждой кости. И, наконец, атрибут animations хранит анимации модели.

Вы используете инвертированную абсолютную конфигурацию кости для преобразования вершин, связанных с этой костью из их системы координат по умолчанию (системы координат модели) в систему координат этой кости, что необходимо для анимации (преобразования) вершин. Мы более подробно исследуем этот процесс в разделе «Формулы скелетной анимации».


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz