使用 Proscenium 框架

阅读数:214 2011 年 10 月 25 日

话题:语言 & 开发

目录

需求

预备知识

您应该拥有使用 ActionScript 编程和使用 Flash Professional 构建 3D 项目的一定经验。完成本教程不需要拥有 Proscenium 框架使用经验。

本教程中引用的示例文件包含在 Proscenium 框架中。

用户水平

中级

需要的产品

示例文件

在本教程中,您将了解一些使用 Proscenium 框架创建 3D 示例项目的不同的 ActionScript 类文件,该框架是一个构建于 Adobe Flash Platform Stage3D API 之上的 ActionScript 3 代码库,支持快速开发交互式 3D 内容。这篇快速介绍的目标是让您熟悉在 Adobe Flash Professional 中使用 Proscenium 框架控制 3D 元素。

开始之前,请确保从 Adobe Labs 下载了 Proscenium 框架,并将该 ZIP 文件解压到了您的桌面上,以便您在学习本教程的每一节时可以找到并打开相应的 AS 文件。您可以分析代码,查看每一步是如何在程序中实现的。

示例项目类文件位于 Proscenium 框架 ZIP 文件的 code\ProsceniumTutorials\src 文件夹中。设置了 Proscenium 项目之后,即可编译和运行这些示例。要设置项目,请确保阅读了示例文件中提供的设置说明。

以下各节介绍如何创建 3D 对象并将它们添加到到场景中以便显示。您还将学习如何更改 3D 对象的材料属性,移动对象和添加阴影。在本教程的最后一节,您将使用一个扩展 Sprite 类的类,查看如何构建一个 Proscenium 应用程序,而不是用其他各节中所使用的 BasicDemo harness 类。通过扩展 Sprite 类,您在使用 3D 对象和配置事件处理函数时将获得更多控制权。

理解世界坐标系统和 3D 照相机

在您阅读使用本示例项目的说明时,请记住世界坐标系统的方向。y 轴始终指向上方。默认情况下,照相机指向 -z 方向,这意味着 x 轴指向屏幕的右侧(参见图 1)。

图 1. 世界坐标系统的方向。

创建简单的 3D 形状并将它们添加到场景中

首先,您将创建一些形状并将它们添加到场景中。如果还未这么做,请确保从 Adobe Labs 下载了 Proscenium 并将该 ZIP 文件解压到您的桌面。打开 Flash Professional 中名为 Tutorial01_SimpleShapes.as 的文件(位于 code\ProsceniumTutorials\src 文件夹中)。第一节的目标是让对象在 3D 场景中显示(参见图 2)。

图 2. 使用 ActionScript 构建形状并将它们放在场景中。

  1. 启动 Flash Professional,打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial01_SimpleShapes.as 的文件。
  2. 花时间分析一下该类中的函数。

代码非常简单。该代码创建一个平面、一个立方体、一个球体和一个圆环,然后将这些 3D 形状添加到场景中。

要创建基本的形状,您将使用 ProceduralGeometry 类,如下面提供的代码示例中所示。

  • MaterialStandard是实现各种渲染模型的 material 类。在本教程中,创建了一种绿色的材料,并使用它作为平面的材料。
  • SceneMesh是一个包含网格的基本的场景图形节点。(其他类型的场景图形节点包含 SceneLight、SceneCamera、SceneSkyBox 等。所有节点类名称都以单词 Scene 开头。
  • BasicDemo.scene是场景图形的根节点。BasicDemo 自动创建一些 SceneLight 对象并包含重要的事件处理函数以实现照相机控制。

创建形状并将它们添加到场景中的代码如下所示:

package
{
   import com.adobe.scenegraph.*;
   
   import flash.display.*;
   import flash.display3D.*;
   import flash.geom.*;
   
   public class Tutorial01_SimpleShapes extends BasicDemo
   {
      public function Tutorial01_SimpleShapes()
      {
         super();
      }
      
      override protected function initModels():void
      {
         // create plane material
         var material:MaterialStandard = new MaterialStandard();
         material.diffuseColor.set( 0, .4, 0 );
         material.specularColor.set( .8, .8, .8 );
         material.ambientColor.set( .2, .2, .2 );

         // create a plane and add it to the scene
         var plane:SceneMesh = MeshUtils.createPlane( 50, 50, 20, 20, 
null, "plane" );
         plane.appendTranslation( 0, -2, 0 );
         scene.addChild( plane );
         
         // create a cube and add it to the scene
         var cube:SceneMesh = MeshUtils.createCube( 5 );
         cube.appendTranslation( 0, 6, 0 );
         scene.addChild( cube );
         
         // create a torus and add it to the scene
         var torus:SceneMesh = MeshUtils.createDonut( .25, 1.5, 50, 10, 
null, "torus" );
         torus.appendTranslation( 10, 2, 0 );
         var rotAxis:Vector3D = new Vector3D( 1,1,1 );
         rotAxis.normalize();
         torus.appendRotation( 45, rotAxis );
         scene.addChild( torus );
         
         // create a sphere and add it to the scene
         var sphere:SceneMesh = MeshUtils.createSphere( 3, 50, 50, 
null, "sphere" );
         sphere.setPosition( -10, 2, 0 );
         scene.addChild( sphere );         
      }
   }
}

创建 3D 对象动画并更改它们的材料属性

在本节中,您将学习如何创建立方体形状的实例。您还将看到如何使用 onAnimate() 函数更改它的位置,基于形状的位置更改它的材料(参见图 3)。

图 3. 更改场景中的形状位置。

  1. 使用 Flash Professional 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial02_AnimationAndMaterial.as 的名称。
  2. 检查该类中的函数。

在检查代码时,请注意 onAnimate() 方法使用了时间t和自上一次调用以来已过去的时间dt来调用。onAnimate() 方法支持您使用 SceneMesh.appendTranslation 方法更改对象的位置。

在此示例中,使用 ProceduralGeometry.createCube 方法创建了一个立方体。使用 SceneMesh.instance() 方法创建了立方体的另一个实例。使用此战略,两个立方体共享相同的顶点和索引缓冲区。

默认情况下,实例化的立方体将使用名为 cubeMtrl 的相同材料。此材料属性可以使用 SceneMesh.materialBindings 更改为使用一种不同的材料。您首先创建一种新材料并将它绑定到实例化的立方体。在这之后,您可以根据需要更改一个实例的材料属性。

override protected function initModels():void
{
   …

   // create a new standard material
   var material:MaterialStandard = new MaterialStandard( "cubeMtrl" );      material.diffuseColor.set( 0, 1, 0 );

   var cube:SceneMesh = MeshUtils.createCube( 5, material, "cube" );
   cube.appendTranslation( 0, 6, -10 );
   scene.addChild( cube );

   // create an instance of the cube (mesh data is shared)
   _cubeInstanced = cube.instance( "cube-instanced" );
   _cubeInstanced.appendTranslation( 0, 6, 0 );
   scene.addChild( _cubeInstanced );

   …         
}

// animation is performed in onAnimate
override protected function onAnimate( t:Number, dt:Number ):void
{
   _cubeInstanced.setPosition( Math.cos( t ) * 3, 6, Math.sin( t ) * 3 );

   // Since a SceneMesh can have multiple submeshes of different materials, 
   // and since the submeshes can be shared amongst multiple SceneMesh instances,
   // direct access to the material is not provided.
   // To change material, one can create a new material and add it to the binding
   // Note that the material name is used to indicate which material is remapped.
   if ( !_cubeInstanced.materialBindings[ "cubeMtrl" ] )
      _cubeInstanced.materialBindings[ "cubeMtrl" ] = new MaterialStandard( "cubeMtrl" );
         
   if ( _cubeInstanced.position.x < 0 )
      _cubeInstanced.materialBindings[ "cubeMtrl" ].diffuseColor.set( 1, 0, 0 );
   else
      _cubeInstanced.materialBindings[ "cubeMtrl" ].diffuseColor.set( 0, 1, 0 );
}

在 3D 场景中定义光线转换器来添加阴影

在本节中,您将探索如何向 3D 场景添加阴影,向光线添加转换器(参见图 4)。

图 4. 阴影为 3D 场景提供了深度。

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial03_Shadows.as 的文件。
  2. 检查该类中的代码。

启用阴影的流程很简单。如果设置了属性 BasicDemo.shadowMapEnabled,启用了 BasicDemo 类的两种默认光线来显示阴影。

下一步是定义光线的转换器。要完成此任务,您可以使用 Proscenium 中一个名为 SceneLight.addToShadowMap 的方法。一种简单的方式是添加 BasicDemo.scene(它将生成光线的场景图形节点转换器)来更新本教程中的 light[1]。

另一种方法是定义每种光线的具体的转换器。例如,在下面的代码中查看是如何独立控制本教中的 lights[0] 和 lights[1] 的。这通常是一个不错的注意,因为这可以确保地面将不会转变来自场景中的 light[0] 的阴影。

override protected function initModels():void
{

   …

   if ( lights )
   {
      lights[0].setPosition( 10, 20, 10);
      if ( lights[0].shadowMapEnabled )
         lights[0].addToShadowMap( _cubeInstanced, cube, torus, sphere );   // define casters
      if ( lights[1].shadowMapEnabled )
         lights[1].addToShadowMap( scene );   // just set every scene graph object as caster
   }
}

从外部文件加载 3D 模型

在本节中,您将学习如何引入在 3D 图形程序中创建的 3D 模型。您可以使用 Proscenium 框架从外部文件夹在 3D 模型。本示例演示了如何加载天空问题和一种 3D 模型(参见图 5)。

图 5. 您可以使用外部模型文件将 3D 模型加载到 Flash 项目中。

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial04_LoadingModels.as 的文件。
  2. 检查类中的代码,查看如何将模型加载到场景中。

名为SceneSkyBox的对象是专为天空提供的场景图形节点对象。要了解关于雾化选项的更多信息,请参阅 TestFog。

6 个天空表面的纹理图像文件名称定义如下:

protected static const SKYBOX_FILENAMES:Vector.<String> = new Vector.<String>(6,true);
    SKYBOX_FILENAMES[ 0 ] = "../res/textures/skybox/px.png";
    SKYBOX_FILENAMES[ 1 ] = "../res/textures/skybox/nx.png";
    SKYBOX_FILENAMES[ 2 ] = "../res/textures/skybox/py.png";
    SKYBOX_FILENAMES[ 3 ] = "../res/textures/skybox/ny.png";
    SKYBOX_FILENAMES[ 4 ] = "../res/textures/skybox/pz.png";
    SKYBOX_FILENAMES[ 5 ] = "../res/textures/skybox/nz.png";

此外,此示例加载一个棕榈树模型。要同时加载天空纹理和棕榈树模型,代码会为每个对象启动一个加载器。当加载完成时,每个加载器发出一个事件。在这个示例项目中,两个加载任务通过以下这些步骤来管理:

  1. 加载天空纹理加载器。
  2. 当加载完成时,调用 imageLoadComplete 处理函数。
  3. 该处理函数创建一个 SceneSkyBox 对象,将它添加到场景中。
  4. 启动棕榈树模型加载器。
  5. 当完成棕榈树模型的加载时,调用 loadComplete 处理函数。
  6. 此处理函数包含将该树模型添加到场景中的代码。

在下面的示例中查看加载外部文件的代码:

override protected function initModels():void
{
   var plane:SceneMesh = MeshUtils.createPlane(
100,100,20,20,null, "plane" );
   plane.transform.appendTranslation( 0, -2, 0 );
   scene.addChild( plane );

   LoadTracker.loadImages( SKYBOX_FILENAMES, imageLoadComplete );
}

protected function imageLoadComplete( bitmaps:Dictionary ):void
{
   var bitmapDatas:Vector.<BitmapData> = new Vector.<BitmapData>( 6, true );
   var bitmap:Bitmap;
   for ( var i:uint = 0; i < 6; i++ )
      bitmapDatas[ i ] = bitmaps[ SKYBOX_FILENAMES[ i ] ].bitmapData;
         
   // sky
   _sky = new SceneSkyBox( bitmapDatas, false );
   scene.addChild( _sky );   // skybox must be an immediate child of scene root
   _sky.name = "Sky"
         
   _treeLoader = new OBJLoader( "../res/models/PalmTrees/PalmTrees.obj" );
   _treeLoader.addEventListener( Event.COMPLETE, loadComplete);
}

protected function loadComplete( event:Event ):void
{
   var tree:SceneNode = new SceneNode( "PalmTrees" );
   var manifest:ModelManifest = _treeLoader.model.addTo( tree );
   scene.addChild( tree );
}

扩展 Sprite 类以创建 Proscenium 应用程序

本教程中描述的所有前述示例使用 BasicDemo 工具改进事件处理函数并添加其他控件。在本教程的最后一节中,您将看到如何在不使用 BasicDemo 工具的情况下创建 Proscenium 应用程序,方法是直接扩展 Sprite 类。

public class Tutorial_SpriteBased extends Sprite

通过扩展 Sprite 类,您获得了对如何向舞台添加子画面的更多控制权。您在创建 Stage3D 上下文,配置和实现事件处理函数时也有更多选项(参见图 6)。

图 6. 您可以扩展 Sprite 类以创建 Proscenium 应用程序。

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial05_SpriteBased.as 的文件。
  2. 检查该类中的代码,查看如何扩展 Sprite 类。

使用 Instance3D 创建 Proscenium 类

Instance3D是一个基本的 Proscenium 类,用于维护 3D Molehill 上下文。您将在希望使用 Proscenium 框架时使用 Instance3D 类。在本例中,以下代码创建一个新的 Instance3D 实例:

instance = new Instance3D( stage3D.context3D );

注意:在前面使用 BasicDemo 类的示例中,它自动创建了一个名为 BasicDemo.instance 的 Instance3D 对象。

设置场景图形根

在以前的教程中,根名为scene,它使用 BasicDemo.scene 设置。但是,因为此示例未使用 BasicDemo 类,所以根只是简单的 Instance3D.scene,在本教程中为 instance.scene。

注意:Instance3D 是一个类名称,instance是一个变量。

终结应用程序

设置了场景图形根之后,创建 3D 模型并将它们添加到场景中的流程涉及到与前面相同的步骤。这个示例创建一种光线,使用转换器实现阴影。另请注意,该实例包含按键和鼠标事件处理函数以处理用户交互。该函数包含打开雾化效果的代码。

使用物理学使物体下落、碰撞和弹跳

在本节中,您将学习如何使物体下落、碰撞和弹跳。

图 7. 使用物理学使物体下落、碰撞和弹跳

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial06_SimplePhysics.as 的文件。
  2. 检查该类中的函数。

物理学将通过创建一个 PelletManager 对象来实现,该对象提供了用于创建启用了物理学的 SceneMesh 对象的方法。例如,可调用 createStaticInfinitePlane 创建地面,立方体可由 createBox 创建,球体可由 createSphere 创建:

    // create a plane and add it to the scene
    var plane:SceneMesh = mSim.createStaticInfinitePlane( 1000, 1000, 2, 2, material, "plane" );
    plane.appendTranslation( 0, -2, 0 );
    scene.addChild( plane );
    
    // create cubes and add it to the scene
    var cube0:SceneMesh = mSim.createBox( 5, 5, 5 );
    cube0.appendRotation( 40, Vector3D.X_AXIS );
    cube0.appendTranslation( 0, 6, 0 );
    scene.addChild( cube0 );
    
    // create a sphere and add it to the scene
    var sphere:SceneMesh = mSim.createSphere( 3, 32, 16 );
    sphere.setPosition( -10, 2, 0 );
    scene.addChild( sphere ); 

向四边形分配纹理

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial07_Texture.as 的文件。
  2. 检查该类中的函数。

纹理图可通过以下代码嵌入:

[Embed( source="/../res/content/foliage022.jpg" )]
    protected static const BITMAP:Class;

使用此数据,可创建一种纹理图并用于漫反射图,如下面的代码中所示:

var textureMap:TextureMap = new TextureMap( new BITMAP().bitmapData ); 
material.diffuseMap = textureMap;

加载 COLLADA 动画文件

在本节中,您将看到加载 COLLADA 动画文件的代码(参见图 8)。

图 8. 加载一个 COLLADA 动画文件

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial08_LoadedAnimation.as 的文件。
  2. 检查该类中的函数。

要加载 COLLADA 文件,参考此代码:

public var loader:ColladaLoader;
override protected function initModels():void {
    loader = new ColladaLoader( "../res/content/AnimatedBones.dae" );
    loader.addEventListener( Event.COMPLETE, onLoad );
    }

当加载完成时,将该模型添加到场景中并将动画绑定到场景:

public var animations:Vector.<AnimationController>, initialized:Boolean;
public function onLoad( event:Event ):void {
    var manifest:ModelManifest = loader.model.addTo( scene );
    animations = loader.model.animations;
    for each ( var anim:AnimationController in animations ) {
        anim.bind( scene );
    }
    
    initialized = true;
}

动画通过递增事件来实现:

override protected function onAnimate( t:Number, dt:Number ):void {
    if ( !initialized ) return;
    for each ( var anim:AnimationController in animations )
    {
        anim.time = ( t % anim.length ) + anim.start;
    }
}

使用过程几何学从头创建网格

  1. 打开 code\ProsceniumTutorials\src 文件夹中名为 Tutorial_09_ProceduralGeometry.as 的文件。
  2. 检查该类中的函数。

延伸阅读

我们希望本教程为向您概述了如何使用 Proscenium 框架为您的 Flash 项目创建 3D 元素。要了解在 Flash Professional 中创建 3D 动画和游戏的更多信息,请查阅以下在线资源:

查看原文:Working with the Proscenium framework