2007年9月28日 @ 15:37
Flex form with a 3d twist(with source)
I just started experimenting with Papervision and wanted to try something I have not seen yet, integrating a Flex view into the 3d realm. There are plenty of samples of really neat looking cars, animals and shapes getting rotated every which way but nothing that shows how to blend something applicationish (is that word?) like a data entry form with 3d to give the user a new experience.
I created a simple component that gets bound to a ViewStack, generates the six sides of a cube and then renders the new cube form. Even though Papervision depends on Bitmaps and static snapshots of things to perform the rendering I was able work editable Flex UI's into the 3d by performing a bit of magic with the ViewStack and positioning.
I have to say, using Papervision to do this stuff is so nice. It only took a few hours to work out the kinks to get this working, and that included figuring out how to use Papervision in the first place. The team who created it deserves a ton of kudos. I'll release the source code soon, I just need to do some cleanup and commenting.
Papervision really opens up a whole new world of potential in Flash/Flex, not just for displaying things, but for how users can even interact with interfaces (even boring data entry forms). It will be exciting to see what people come up with, imagination is the only limit.
As promised here is the source code for the 3d flex form component I created using the Papervision engine. You can download the entire project sample code here. I should have some feature updates to this in the near future. Enjoy.
/**************************************************************
CubeForm.as
Created by: Derrick Grigg
derrick@dgrigg.com
http://www.dgrigg.com
Created on: April 18, 2007
Version: 1.0.0
This is released under a Creative Commons license. More information can be found here:
http://creativecommons.org/licenses/by/2.5/
***************************************************************/
package com.dgrigg.containers
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.utils.clearInterval;
import flash.utils.setInterval;
import flash.geom.Point;
import flash.events.Event;
import mx.core.UIComponent;
import mx.containers.ViewStack;
import mx.core.Container;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.objects.Plane;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.materials.BitmapMaterial;
/**
* Dispatched after cube rotates to selected side.
*/
[Event(name="change",type="flash.events.Event")]
/**
* Renders a view stack as a 3d cube using the PaperVision engine.
* The cube can rotate to the 6 different sides to display a view
* from the view stack and allow the user to interact with it.
*/
public class CubeForm extends UIComponent
{
/**
* Papervision camerea.
*/
protected var camera:Camera3D;
/**
* Papervision scene.
*/
protected var scene:Scene3D;
/**
* Sprite that form is rendered in.
*/
protected var sprite:Sprite;
/**
* Value to store a reference to the rotation interval.
*/
protected var intRotate:int;
/**
* @private
*/
protected var _selectedIndex: int = 0;
/**
* Array of the six planes that make up the cube.
* Each item in the array consists of a <code>plane</code> which is the Papervision Plane
* and a <code>view</code> which is a reference to the child in the view stack.
*/
protected var planes:Array;
/**
* The view stack the form is bound to.
*/
protected var _viewStack:ViewStack;
/**
* Root display object for the cube form.
*/
protected var rootNode:DisplayObject3D;
/**
* Number of steps used to rotate the cube;
*
* @default 10
*/
public var rotationSteps:int = 10;
/**
* @param canvas The sprite to generate the cube form in.
*/
public function CubeForm(canvas:Sprite)
{
sprite = canvas;
init();
}
private function init():void
{
var s:Stage = sprite.stage;
s.quality = StageQuality.BEST;
setupScene();
}
private function setupScene():void
{
scene = new Scene3D(sprite);
camera = new Camera3D();
camera.zoom = 10;
camera.focus = 100;
rootNode = scene.addChild(new DisplayObject3D(), 'rootNode');
scene.renderCamera(camera);
}
/**
* The view stack that is bound to the cube form.
* <ul>
* <li>The view stack must have atleast six children, otherwise an error will be thrown.</li>
* <li>The view stack must have it's <code>creationPolicy</code> property set to <code>all</code>.</li>
* <li>Each view in the view stack must have it's width and height explicity set,
* otherwise the view does not get rendered properly during initialization.</li>
* </ul>
*
*/
public function set viewStack (vs:ViewStack):void
{
var plane: Plane;
var view:Container;
planes = new Array();
_viewStack = vs;
//check to see if there are 6 views in the view stack
//if no throw an error
var children:Array = _viewStack.getChildren();
if (children.length > 5)
{
view = children[0] as Container;
plane = createPlane(view, 0 , 0 ,-(_viewStack.width/2));
rootNode.addChild(plane, 'plane0');
planes.push({plane:plane, view:view});
view = children[1] as Container;
plane = createPlane(view, (_viewStack.width/2), 0, 0, 0, 270, 0);
rootNode.addChild(plane, 'plane1');
planes.push({plane:plane, view:view});
view = children[2] as Container;
plane =createPlane(view, 0, 0, (_viewStack.width/2), 0, 180, 0);
rootNode.addChild(plane, 'plane2');
planes.push({plane:plane, view:view});
view = children[3] as Container;
plane =createPlane(view, -(_viewStack.width/2), 0, 0, 0, 90, 0);
rootNode.addChild(plane, 'plane3');
planes.push({plane:plane, view:view});
view = children[4] as Container;
plane =createPlane(view, 0, (_viewStack.width/2), 0, 270, 0, 0);
rootNode.addChild(plane, 'plane4');
planes.push({plane:plane, view:view});
view = children[5] as Container;
plane =createPlane(view, 0, -(_viewStack.width/2), 0, 90, 0, 0);
rootNode.addChild(plane, 'plane5');
planes.push({plane:plane, view:view});
//render the scene to get determine the rendered dimensions
scene.renderCamera( camera );
//position the view stack directly over the cube form
var p:Point = sprite.localToGlobal(new Point(0,0));
_viewStack.x = p.x - _viewStack.width/2;
_viewStack.y = p.y - _viewStack.height/2;
//adjust the camera zoom to compensate for any scaling that has occurred,
//in order to keep the cube at the exact scale of the view stack
camera.zoom = (_viewStack.width/sprite.width)*10;
scene.renderCamera(camera);
} else {
var error:Error = new Error('Error: the viewStack must contain at least six children');
throw(error);
}
}
/**
* @private
*/
protected function createPlane(panel:UIComponent, pX:int=0, pY:int=0, pZ:int=0, pRotationX:int=0, pRotationY:int=0, pRotationZ:int=0):Plane
{
var bmp:BitmapData = new BitmapData(_viewStack.width, _viewStack.height);
bmp.draw(panel);
var material:BitmapMaterial = new BitmapMaterial(bmp);
material.smooth = true;
var initObj:Object = {x:pX, y:pY, z:pZ, rotationX:pRotationX, rotationY: pRotationY, rotationZ:pRotationZ};
var plane:Plane = new Plane(material, bmp.width, bmp.height, 4, 4, initObj);
return plane;
}
/**
* Index of selected plane on the cube form.
*
* @default 0
*/
public function get selectedIndex():int
{
return _selectedIndex;
}
public function set selectedIndex(val:int):void
{
//get the selected plane and re-render that side in order
//to reflect the current data on the view
var obj:Object = planes[_selectedIndex];
var plane:Plane = obj.plane as Plane;
var bmp:BitmapData = new BitmapData(obj.view.width, obj.view.height);
bmp.draw(obj.view);
var material:BitmapMaterial = new BitmapMaterial(bmp);
plane.material = material;
this.scene.renderCamera( camera );
obj.view.visible = false;
//get the current cube's rotation values
_selectedIndex = val;
var curRotation:Object = {x:rootNode.rotationX, y:rootNode.rotationY, z:rootNode.rotationZ};
var destRotation:Object;
switch (_selectedIndex)
{
case 0:
destRotation = {x: 0, y:0, z:0};
break;
case 1:
destRotation = {x: 0, y:90, z:0};
break;
case 2:
destRotation = {x: 0, y:180, z:0};
break;
case 3:
destRotation = {x: 0, y:270, z:0};
break;
case 4:
destRotation = {x: 90, y:0, z:0};
break;
case 5:
destRotation = {x: 270, y:0, z:0};
break;
}
var steps: Object = new Object();
steps.x = Math.floor((destRotation.x - curRotation.x)/rotationSteps);
steps.y = Math.floor((destRotation.y - curRotation.y)/rotationSteps);
steps.z = Math.floor((destRotation.z - curRotation.z)/rotationSteps);
intRotate = setInterval(rotateCube, 50, steps, destRotation);
}
private function rotateCube(steps:Object, dest:Object):void
{
if (Math.abs(rootNode.rotationX) != dest.x) rootNode.rotationX += steps.x;
if (Math.abs(rootNode.rotationY) != dest.y) rootNode.rotationY += steps.y;
if (Math.abs(rootNode.rotationX) != dest.z) rootNode.rotationZ += steps.z;
scene.renderCamera( camera );
if (Math.abs(rootNode.rotationX) == dest.x && Math.abs(rootNode.rotationY) == dest.y && Math.abs(rootNode.rotationZ) == dest.z)
{
//reset the rotation values back to a value <= 360
if (Math.abs(rootNode.rotationX) >= 360) rootNode.rotationX = Math.abs(rootNode.rotationX) - 360;
if (Math.abs(rootNode.rotationY) >= 360) rootNode.rotationY = Math.abs(rootNode.rotationY) - 360;
if (Math.abs(rootNode.rotationZ) >= 360) rootNode.rotationZ = Math.abs(rootNode.rotationZ) - 360;
clearInterval(intRotate);
_viewStack.selectedIndex = _selectedIndex;
dispatchEvent(new Event(Event.CHANGE));
}
}
}
}
Filed under: 资料 · Tags: 3D form pv3d
Articles related:
Away3D Smooth shading and lightning (v2) (2007-9-26 22:13:0)
Flex/AS3 3D Engine demo (2007-9-26 16:39:33)
关于表单可用性的一些资料和想法二:别具一格的布局和愉悦的交互 (2007-9-9 8:24:34)
Papervision3D 资料汇总 (2007-8-21 14:41:14)
如何使用Papervision3d(更新,工具改用 Flex 2) (2007-7-11 10:8:14)
Leave a Comment
◎welcome to give out your point。

Posted by Dmitriy @ 2010-4-2 11:46:45
http://www.animusdius.ru
Hey Derrick,
Thanks for this nice example. I was looking for something like this to get started with a UI I am building ofr our jee application. It's so kind to provide the sample code.
Reply this comment