четверг, 4 августа 2011 г.

Основы PaperVision3D. Введение и создание шаблона

Всем привет, с вами снова noTformaT, и мы продолжаем изучать основы 3D движка «PaperVision3D». Прошлая статья была «первым знакомством с PaperVision3D»,  а в этой статье мы с вами детально рассмотрим элементы, по которым мы бегло пробежались в предыдущей статье, и создадим шаблон, который мы будем использовать с следующих статьях.
В прошлой статье, как и в этой в качестве IDE будет использоваться FlashDevelop версии 3.3.4 (вместе с которым идет FLEX SDK), хотя любая другая версия (современная) должна подойти. Если что, то мой FlashDevelop выглядит примерно так:
Так же нам понадобится библиотека PaperVision3D, в прошлой статье я описывал, как ее скачать и в какую директорию копировать, вроде бы это была «D:\pv3d_tut». 
Имея все это, мы можем приступить к дальнейшему изучению PaperVision3D, но вначале надо настроить проект. Создадим проект, делается это очень просто, на напели «Project» выбираем пункт «Create Project» или через меню FlashDevelop’a «Project->New Project».
Перед нами появится окно создания нового проекта. На панели «Installed Templates» выбираем тип проекта «AS3 Project». В поле «Name» указываем название, я назвал его «PV3D_Patern», так как мы сейчас делаем шаблон для наших будущих приложения, то название подходящее, а в поле «Location» указываем директорию в котором будет распологатся проект, в моем случае это «D:\pv3d_tut\tut_2», ну, а целом все выглядит так:
Далее жмем на кнопку «ОК» и наш проект создается. Теперь надо подключить к этому проекту библиотеку PaperVision3D, делается это просто, заходим в меню «Project->Properties», откроется окно настройки проекта, далее перейдем во вкладку «ClashPaths», и нажмем на кнопку «Add ClashPath», появится окно, в котором выберем местоположение библиотеки PaperVision3D. В моем случае это выглядит так:
На панели «Project» мы увидим, что в наш проект добавлена библиотека PaperVision3D со всеми нужными пакетами, так же нам понадобится класс Main.as, который лежит в папке проекта «src»:
Давайте откроем этот класс, и посмотрим из чего он состоит, в целом в нем должен быть следующий код:
package 
{
 import flash.display.Sprite;
 import flash.events.Event;
 
 /**
  * ...
  * @author noTformaT
  */
 public class Main extends Sprite 
 {
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point
  }
  
 }
 
}
Как видно, этот класс расширяет класс «Sprite», и имеет один метод «Init» и конструктор класса «Main». Все инициализировать мы будет в методе «init», но сейчас нам надо импортировать соответствующие классы и пакеты, вот весь список нужных «импортов»:
import flash.display.Sprite;
import flash.events.Event;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.objects.primitives.Sphere;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.view.Viewport3D;
Далее надо описать соответствующие свойства класса «Main». Первое свойство это _scene, это объект класса org.papervision3d.scenes.Scene3D, это основной класс сцены, в ней мы будет располагать все 3D модели. _viewPort – это свойство отвечает за отображение сцены на поверхности, другими словами, это поверхность на которую будем визуализиваровать сцену с 3D объектами. _viewPort это объект класса org.papervision3d.view.Viewport3D. Следующее свойство - это объект _camera класса org.papervision3d.cameras.Camera3D, _camera будет визуализировать все, что попадает в ее видимость на сцене. Теперь добавим свойство _renderEngine, это самый главный объект который визуализирует всю сцену, это ядро PaperVision3D. Вот как бы и все нужные свойства, теперь осталось написать метод, который будет инициализировать все эти компоненты. Назовем его «InitPV3D», вот весь код этого метода:
private function InitPV3D():void 
{
 // создаем движек, который будет все рендерить
 _renderEngine = new BasicRenderEngine();
 //создаем вьюпорт размером 800х600
 _viewPort = new Viewport3D(800, 600);
 //добавляем вьюпорт в отображение флешки
 addChild(_viewPort);
 //создаем сцену
 _scene = new Scene3D();
 //создаем камеру
 _camera = new Camera3D();
}
Хоть в коде этого метода есть много комментариев, я все же расскажу, что происходит в этом методе. В первой строчке мы создаем объект _renderEngine, далее создаем объект _viewPort разметом 800х600, как я говорил выше, это обычная поверхность, а если быть точнее, то это наследник класса Sprite, тоесть это обычный спрайт. Для того чтобы мы выдели результат рендера нам надо добавить _viewPort в список отображение, делается это обычно методом addChild(). Для тех кто не понял что такое «список отображения» то советую почитать литературу по ActionScript 3.0, иначе очень многое для вас будет непонятным. Далее мы создаем сцену - _scene и камеру - _camera. Вот и все, вся инициализация компонентов.
Теперь нам надо добавить метод «InitPV3D» в метод «init», сразу же после комментария «// entry point». Далее надо описать метод-слушатель события «Event.ENTER_FRAME». Назовем его «OnEnterFrameListener», вот весь его код:
private function OnEnterFrameListener(e:Event):void
{
 //тут будет код, который должен выполнится в каждом кадре
 _renderEngine.renderScene(_scene, _camera, _viewPort);
}
В нем есть комментарий, по которому можно понять что «там мы будем писать код, который должен вызываться каждый кадр». Далее идет код рендеринга сцены, рендер сцены происходит с помощью метода renderScene, для этого надо иметь 4 компонента: сам движек который вызовет этот метод, камеру, сцену и поверхность на которую будем рендерить сцену. Вот и все, все эти компоненты есть. Осталось только добавить слушатель на событие «Event.ENTER_FRAME» в конце метода «init», после вызова метода инициализации PaperVision3D «InitPV3D». На всякий случай вот полный код метода «init»:
private function init(e:Event = null):void 
{
 removeEventListener(Event.ADDED_TO_STAGE, init);
 // entry point
 //Инициализируем PaperVision3D
 InitPV3D();
 //ставим слущатель на событие Event.ENTER_FRAME
 addEventListener(Event.ENTER_FRAME,  OnEnterFrameListener);
 //тут будут другие методы которые инициализации других данных
}
Все, нужный нам шаблон готов. Тут есть все что надо: инициализация нужных нам компонентов для работы с «PaperVision3D», и рендер сцены в каждом кадре. В итоге файл «Main.as» должен содержать следующий код:
package 
{
 import flash.display.Sprite;
 import flash.events.Event;
 import org.papervision3d.cameras.Camera3D;
 import org.papervision3d.render.BasicRenderEngine;
 import org.papervision3d.scenes.Scene3D;
 import org.papervision3d.view.Viewport3D;

 
 /**
  * ...
  * @author noTformaT
  */
 public class Main extends Sprite 
 {
  private var _scene:Scene3D = null; //сцена в которую будем добавлять 3д объекты
  private var _viewPort:Viewport3D = null; //DisplayObject в который будем рендерить
  private var _camera:Camera3D = null; //Камера
  private var _renderEngine:BasicRenderEngine = null; //рендерный движек
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  
  /**
   * Метод инициализирует объект класса Main, в конструкторе (если объект это stage) или после события Event.ADDED_TO_STAGE
   * @param e - парамерт который генерируется диспатчером
   */
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point
   //Инициализируем PaperVision3D
   InitPV3D();
   //ставим слущатель на событие Event.ENTER_FRAME
   addEventListener(Event.ENTER_FRAME,  OnEnterFrameListener);
   //тут будут другие методы инициализации других данных
  }
  
  /**
   * Метод инициализует компоненты PaperVision3D
   */
  private function InitPV3D():void 
  {
   // создаем движек, который будет все рендерить
   _renderEngine = new BasicRenderEngine();
   //создаем вьюпорт размером 800х600
   _viewPort = new Viewport3D(800, 600);
   //добавляем вьюпорт в отображение флешки
   addChild(_viewPort);
   //создаем сцену
   _scene = new Scene3D();
   //создаем камеру
   _camera = new Camera3D();
  }
  
  /**
   * метод-слушатель события Event.ENTER_FRAME
   * @param e - парамерт который генерируется диспатчером
   */
  private function OnEnterFrameListener(e:Event):void
  {
   // тут будет код, который должен выполнится в каждом кадре
   _renderEngine.renderScene(_scene, _camera, _viewPort);
  }
  
 }
 
}
И так, шаблон у нас готов, и теперь сохраняя нашу «проделанную работу» мы можем использовать этот шаблон в следующих проектах.
Сейчас мы возьмем написанный нами шаблон, и с помощью его попытаемся сделать новый проект. В следующем проекте мы просто добавим сферу на сцену, и будем ее вращать в каждом кадре. И так, приступим. Для начала сделаем дубликат папки, где хранится шаблон, и назовем эту папку «tut_3».
Зайдем в эту папку и откроем проект для FlashDevelop’a под названием «PV3D_Patern.as3proj». Как видно там ничего не изменилось, это все еще тот же шаблон что мы писали выше. Теперь опять откроем файл «Main.as» и опишем в этом классе следующее свойство:
private var _sphere:Sphere;
Это примитивная сфера класса org.papervision3d.objects.primitives.Sphere, в следующей статье мы с вами рассмотрим все примитивные объекты, которые предоставляет PaperVision3D. Так же надо добавить импорт класса примитивной сферы:
import org.papervision3d.objects.primitives.Sphere;
И так, теперь о том, зачем же нам нужна сфера. В этом примере я покажу, как добавляются объекты на сцену, а в качестве объекта буду использовать простую сферу. Все добавляемые объекты на сцену это объекты класса org.papervision3d.objects.DisplayObject3D, а вернее его наследники, примитивная сфера является наследником этого класса. Теперь нам надо инициализировать сферу и добавить ее на сцену, для этого весь этот код выведем в отдельный метод под названием «InitAndAddSpreheToScene», вот весь код этого метода:
private function InitAndAddSphereToScene():void
{
//создаем сферу
 _sphere = new Sphere();
 //добавляем ее на сцену
 _scene.addChild(_sphere);
}
Сейчас я объясню, что в нем да как, в первой строчке мы создаем объект Sphere, сейчас мы не будем разбирать все параметры конструктора Sphere, это мы сделаем в следующей статье, а сейчас мы всего лишь должны понимать, что создастся сфера, с материалом, который показывает сетку объекта, со случайным цветом, радиусом в 100 делений, и  с 8 сегментами по вертикали  и 6 сегментами по горизонтали, именно такие параметры по умолчанию у этого конструктора, но не будем об этом. Во второй строке, мы добавили сферу на сцену _scene через метод addChild(), так же мы добавляли камеру в прошлый раз. Вот и весь код метода «InitAndAddSpreheToScene». Теперь давайте добавим вызов этого метода в метод «init», сразу же после комментария
//тут будут другие методы инициализации других данных
Если сейчас скомпилировать и запустить код, то мы увидим что-то наподобие этого:
Мы видим с вами сферу с теми параметрами, которые я описал выше. Ну, а теперь как я и обещал, сейчас мы заставим сферу крутится. Крутить сферу будет в слушателе  «OnEnterFrameListener» события «Event.ENTER_FRAME», поэтому после комментария
//тут будет код, который должен выполнится в каждом кадре
Вставляем следующий код:
_sphere.localRotationY += 5;
Этот код увеличивает локальный угол поворота сферы по оси у. В  «PaperVision3D» есть локальные и глобальные координаты, локальные зависят от объекта, в котором расположен объект, то есть сфера будет вращаться в сцене, об локальных и глобальных координатах мы с вами поговорим попозже.
Если сейчас запустить код проекта, то мы увидим как сфера вращается вогруг оси у.
Вот и все, в этой статье мы с вами написали шаблон в котором инициализируются все главные компоненты PaperVision3D и использовали шаблон для создания проекта со сферой. Исходный код шаблона можно скачать тут, а исходный код проекта со сферой тут.
Если есть какие то вопросы по статье, то пишите их в комментариях к статье.
Все, всем пока, с вами был noTformaT. Удачи.