[Box2D][PV3D] 3D Ball Joint
===================================================
Box23DAS3 and Papervision3D Demo
* Please drag stage balls
*
* ボールをジョイントでつないでみました
* 注:Line3Dがリークしていて時間経過で負荷が高まっていきます>_<
*
* @author Yasu
* @see http://clockmaker.jp/blog/
* @since 2009.04.05
===================================================
/**
* Copyright clockmaker ( http://wonderfl.net/user/clockmaker )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/Afcu
*/
/*===================================================*//**
* Box23DAS3 and Papervision3D Demo
* Please drag stage balls
*
* ボールをジョイントでつないでみました
* 注:Line3Dがリークしていて時間経過で負荷が高まっていきます>_<
*
* @author Yasu
* @see http://clockmaker.jp/blog/
* @since 2009.04.05
*//*===================================================*/
package
{
import Box2D.Collision.Shapes.*;
import Box2D.Collision.b2AABB;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.Joints.*;
import Box2D.Dynamics.*;
import flash.net.*;
import flash.ui.*;
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import org.papervision3d.events.*;
import org.papervision3d.lights.*;
import org.papervision3d.materials.shadematerials.*;
import org.papervision3d.objects.*;
import org.papervision3d.view.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.core.geom.renderables.*;
import org.papervision3d.core.geom.*;
import org.papervision3d.materials.special.*;
import net.hires.debug.Stats;
[SWF(width="464", height="465", frameRate="30")]
public class Main extends BasicView
{
// const vars
static public const OBJ_SIZE:int = 40;
static public const OBJ_NUM:uint = 10;
static public const OBJ_COLOR:uint = 0x3399FF;
// vars for Box2D
private var worldWidth:Number;
private var worldHeight:Number;
private var m_iterations:int;
private var m_wallWidth:Number;
private var m_wallHeight:Number;
private var m_timeStep:Number;
private var m_physScale:Number;
private var m_world:b2World;
private var m_mouseJoint:b2MouseJoint;
private var m_draggedBody:b2Body;
private var mouseXWorldPhys:Number;
private var mouseYWorldPhys:Number;
private var isMouseDown:Boolean;
private var arrayIndex:int;
// array of objs
private var pv3dObjsArr:Vector.<DisplayObject3D> = new Vector.<DisplayObject3D>(OBJ_NUM, true);
private var box3dSpapesArr:Vector.<b2Body> = new Vector.<b2Body>(OBJ_NUM, true);
private var joints:Vector.<b2DistanceJoint> = new Vector.<b2DistanceJoint>(OBJ_NUM, true);;
private var line3D:Line3D;
private var lines3D:Lines3D;
private var lm:LineMaterial;
/**
* Constructor
*/
public function Main()
{
stage.quality = StageQuality.LOW;
// init PV3D
super(465, 465, false, true);
// for poligon cross problem, but it's heavy...
//renderer = new QuadrantRenderEngine(QuadrantRenderEngine.QUAD_SPLIT_FILTER);
// init PV3D World
createPaervision3dWorld();
// init Box2D World
createBox2dWorld()
// init vars for drag
arrayIndex = -1;
isMouseDown = false;
// addEvent
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
// Backgound
createBackGround();
cerateFullScreenBtn();
// debug
addChild(new Stats)
}
/**
* Create Papervision3D World
*/
private function createPaervision3dWorld():void
{
camera.zoom = 1000 / camera.focus + 1;
camera.x = 0;
camera.z = -1000;
// create light
var light:PointLight3D = new PointLight3D();
light.z = -100;
for(var i:int=0; i < OBJ_NUM; i++)
{
var mat:FlatShadeMaterial
if(i == 0) mat = new FlatShadeMaterial(light, 0x990000);
else mat = new FlatShadeMaterial(light, OBJ_COLOR);
mat.interactive = true;
var obj3d:DisplayObject3D = scene.addChild(new Sphere(mat, OBJ_SIZE/2, 6, 5));
obj3d.extra = { radius:OBJ_SIZE, arrayPos:i };
obj3d.x = - Math.random() * 200 + 100;
obj3d.y = Math.random() * -200;
obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, obj3dMousePressHandler);
obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, obj3dMouseOverHandler);
obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, obj3dMouseOutHandler);
pv3dObjsArr[i] = obj3d;
}
// lines
lines3D = new Lines3D();
scene.addChild(lines3D);
lm = new LineMaterial(0xFFFFFF);
}
/**
* Create Box2D World
*/
private function createBox2dWorld():void
{
// init Box2D
worldWidth = stage.stageWidth;
worldHeight = stage.stageHeight;
m_iterations = 5;
m_timeStep = 1 / stage.frameRate;
m_physScale = 60;
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set( -1000, -1000);
worldAABB.upperBound.Set(1000, 1000);
var gravity:b2Vec2 = new b2Vec2(0, 10);
var doSleep:Boolean = true;
m_world = new b2World(worldAABB, gravity, doSleep);
// craete wall for Box2D
var wallShapeDef:b2PolygonDef = new b2PolygonDef();
var wallBodyDef:b2BodyDef = new b2BodyDef();
var wall:b2Body;
m_wallWidth = stage.stageWidth;
m_wallHeight = stage.stageHeight;
// left wall
wallShapeDef.SetAsBox(10 / m_physScale, m_wallHeight / 2 / m_physScale);
wallBodyDef.position.Set(0, m_wallHeight / 2 / m_physScale);
wall = m_world.CreateBody(wallBodyDef);
wall.CreateShape(wallShapeDef);
// right wall
wallBodyDef.position.Set(m_wallWidth / m_physScale, m_wallHeight / 2 / m_physScale);
wall = m_world.CreateBody(wallBodyDef);
wall.CreateShape(wallShapeDef);
// upper wall
wallShapeDef.SetAsBox(m_wallWidth / 2 / m_physScale, 10 / m_physScale);
wallBodyDef.position.Set(m_wallWidth / 2 / m_physScale, 0);
wall = m_world.CreateBody(wallBodyDef);
wall.CreateShape(wallShapeDef);
// bottom wall
wallBodyDef.position.Set(m_wallWidth / 2 / m_physScale, m_wallHeight / m_physScale);
wall = m_world.CreateBody(wallBodyDef);
wall.CreateShape(wallShapeDef);
wall.SetMassFromShapes();
for(var i:int=0; i < OBJ_NUM; i++)
{
var obj3d:DisplayObject3D = pv3dObjsArr[i];
var boxShape:b2CircleDef = new b2CircleDef();
boxShape.radius = OBJ_SIZE / m_physScale / 2;
boxShape.density = 3;
boxShape.friction = 10;
boxShape.restitution = 0.75;
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set((obj3d.x + worldWidth / 2) / m_physScale, (obj3d.y + worldHeight / 2) / m_physScale);
var body:b2Body = m_world.CreateBody(bodyDef);
body.CreateShape(boxShape);
body.SetUserData(obj3d);
body.SetMassFromShapes();
box3dSpapesArr[i] = body;
}
// create joints
for (i = 1; i < box3dSpapesArr.length; i++)
{
// joint to balls from drag obj
var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
jointDef.Initialize(
box3dSpapesArr[0],
box3dSpapesArr[i],
box3dSpapesArr[0].GetPosition(),
box3dSpapesArr[i].GetPosition());
var joint:b2DistanceJoint = m_world.CreateJoint(jointDef) as b2DistanceJoint;
joint.m_length = 1;
joint.m_frequencyHz = 1.5;
joint.m_dampingRatio = 0;
}
}
/**
* get mouse position, and convert box2d scale
*/
private function updateMouseWorld():void
{
mouseXWorldPhys = mouseX / m_physScale;
mouseYWorldPhys = mouseY / m_physScale;
}
/**
* Enter Frame
* @param event
*/
private function enterFrameHandler(event:Event):void
{
// update Box2D step
updateMouseWorld();
mouseDrag();
m_world.Step(m_timeStep, m_iterations);
// sync position to PV3D from Box2D
for (var bb:b2Body = m_world.GetBodyList(); bb; bb = bb.GetNext())
{
if (bb.GetUserData()is DisplayObject3D)
{
bb.GetUserData().x = bb.GetPosition().x * m_physScale - worldWidth / 2;
bb.GetUserData().y = -bb.GetPosition().y * m_physScale + worldHeight / 2;
bb.GetUserData().rotationZ = -bb.GetAngle() * (180 / Math.PI);
}
}
// lines
lines3D.removeAllLines();
for (var i:int = 1; i < pv3dObjsArr.length; i++)
{
var os:DisplayObject3D = pv3dObjsArr[0];
var oe:DisplayObject3D = pv3dObjsArr[i];
var startV:Vertex3D = new Vertex3D(os.x, os.y, os.z);
var endV:Vertex3D = new Vertex3D(oe.x, oe.y, oe.z);
var line:Line3D = new Line3D(lines3D, lm, 1, startV, endV);
lines3D.addLine(line);
}
singleRender();
}
/**
* Drag And Drop
*/
private function mouseDrag():void
{
if (isMouseDown && ! m_mouseJoint)
{
m_draggedBody = null;
if (arrayIndex > -1)
m_draggedBody = box3dSpapesArr[arrayIndex];
if (m_draggedBody)
{
var md:b2MouseJointDef = new b2MouseJointDef();
md.body1 = m_world.GetGroundBody();
md.body2 = m_draggedBody;
md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
md.maxForce = 3000 * m_draggedBody.GetMass();
md.timeStep = m_timeStep;
m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
m_draggedBody.WakeUp();
}
}
if (!isMouseDown)
{
if ( m_mouseJoint )
{
m_world.DestroyJoint( m_mouseJoint );
m_mouseJoint = null;
}
}
if ( m_mouseJoint )
{
var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
m_mouseJoint.SetTarget(p2);
}
}
/**
* Mouse Down
* @param event
*/
private function mouseDownHandler(event:MouseEvent):void
{
isMouseDown = true;
}
/**
* Mouse Up
* @param event
*/
private function mouseUpHandler(event:MouseEvent):void
{
Mouse.cursor = MouseCursor.ARROW;
isMouseDown = false;
arrayIndex = -1;
}
/**
* get number of pv3d clicked obj
* @param event
*/
private function obj3dMousePressHandler(event:InteractiveScene3DEvent):void
{
arrayIndex = (event.target as DisplayObject3D).extra.arrayPos;
}
private function obj3dMouseOverHandler(e:InteractiveScene3DEvent):void
{
Mouse.cursor = MouseCursor.HAND;
}
private function obj3dMouseOutHandler(e:InteractiveScene3DEvent):void
{
if(!isMouseDown)
Mouse.cursor = MouseCursor.ARROW;
}
private function createBackGround():void
{
var bgMatrix:Matrix = new Matrix();
bgMatrix.rotate(90 * Math.PI / 180);
graphics.beginGradientFill("linear", [0xFFFFFF, 0x001122], [100, 100], [0, 255], bgMatrix);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
}
private function cerateFullScreenBtn():void
{
var btn:Sprite = new Sprite();
var loader:Loader = new Loader();
loader.load(new URLRequest("http://wonderfl.kayac.com/img/code/out_arrow_o.gif"));
btn.addChild(loader);
btn.x = 435;
btn.y = 5;
btn.buttonMode = true;
btn.addEventListener(MouseEvent.CLICK, function():void
{
if (stage.displayState == StageDisplayState.FULL_SCREEN)
stage.displayState = StageDisplayState.NORMAL;
else
stage.displayState = StageDisplayState.FULL_SCREEN;
});
addChild(btn);
}
}
}