/** * PV3D Bezier Ribbons * @author Marco Di Giuseppe * @see http://designmarco.com * @since 5/6/09 */ package { import flash.events.Event; import flash.events.MouseEvent; import org.papervision3d.view.BasicView; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import net.hires.debug.Stats; [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="60")] public class BezierRibbons extends BasicView { private static const NUM_RIBBONS:int = 3; private var mouseDown:Boolean; private var ribbons:Array; private var ribbonColors:Array; private var xpos:Number; private var ypos:Number; private var ribbon:Ribbon; public var holder:DisplayObject3D; public function BezierRibbons() { super(465, 465); init(); } private function init():void { camera.focus = 11; camera.zoom = 100; holder = new DisplayObject3D(); scene.addChild(holder); ribbonColors = [0xFF0000, 0xFFFFFF, 0x33CCFF]; ribbons = []; var i:int = NUM_RIBBONS; while( --i > -1 ) { ribbon = new Ribbon(this, ribbonColors[i]); ribbons[ribbons.length] = ribbon; } stage.addEventListener(Event.ENTER_FRAME, render, false, 0, true); stage.addEventListener(MouseEvent.MOUSE_UP, mouseHandler, false, 0, true); stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseHandler, false, 0, true); addChild(new Stats()); } private function render(event:Event):void { xpos = ((stage.mouseX - stage.stageWidth * 0.5) / stage.stageWidth) * 180; ypos = ((stage.mouseY - stage.stageHeight * 0.5) / stage.stageHeight) * 180; if (mouseDown) { holder.rotationY += (xpos - holder.rotationY) * 0.2; holder.rotationX += (ypos - holder.rotationX) * 0.2; holder.rotationZ += (ypos - holder.rotationZ) * 0.2; } else holder.roll(0.1); singleRender(); } private function mouseHandler(event:MouseEvent):void { mouseDown = !mouseDown; } public function reset():void { var i:int = NUM_RIBBONS; while( --i > -1 ) { ribbons[i].remove(); } init(); } } } import flash.utils.Timer; import flash.events.TimerEvent; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.objects.DisplayObject3D; class Ribbon extends DisplayObject3D { private static const RIBBON_LENGTH:Number = 300; private static const RIBBON_WIDTH:Number = 3; private static const CURVE_QUALITY:int = 50; private static const NUM_CURVES:int = 5; private var ribbon:BezierRibbons; private var stepId:int; private var curves:Array; private var points:Array; private var color:uint; private var cLen:int; private var curve:Curve; public function Ribbon(ribbon:BezierRibbons, color:uint) { this.ribbon = ribbon; this.color = color; this.ribbon.holder.addChild(this); curves = []; points = []; points[points.length] = new Vertex3D(); points[points.length] = new Vertex3D(); points[points.length] = new Vertex3D(); var timer:Timer = new Timer(10); timer.addEventListener(TimerEvent.TIMER, onRender, false, 0, true); timer.start(); addCurve(); } public function onRender(event:TimerEvent):void { cLen = curves.length; curves[cLen - 1].addLine(); if (cLen > NUM_CURVES - 1) curves[0].removeLine(); if (stepId++ > CURVE_QUALITY) addCurve(); } private function addCurve():void { addNextPoint(); var pLen:int = points.length; var nextPt:Vertex3D = points[pLen - 1]; var curPt:Vertex3D = points[pLen - 2]; var lastPt:Vertex3D = points[pLen - 3]; var xx:Number = (curPt.x + lastPt.x) * 0.5; var yy:Number = (curPt.y + lastPt.y) * 0.5; var zz:Number = (curPt.z + lastPt.z) * 0.5; var mx:Number = (curPt.x + nextPt.x) * 0.5; var my:Number = (curPt.y + nextPt.y) * 0.5; var mz:Number = (curPt.z + nextPt.z) * 0.5; var lastMidPt:Vertex3D = new Vertex3D(xx, yy, zz); var midPt:Vertex3D = new Vertex3D(mx, my, mz); curve = new Curve(lastMidPt, midPt, curPt, color, RIBBON_WIDTH, CURVE_QUALITY); curves[curves.length] = curve; addChild(curve); var oldCurve:Curve; var cLen:int = curves.length; if (cLen > NUM_CURVES) { oldCurve = curves.shift(); removeChild(oldCurve); oldCurve.remove(); points.shift(); } stepId = 0; } private function addNextPoint():void { var xdelta:Number = randomInRange(0, RIBBON_LENGTH); if (Math.random() < 0.5) xdelta = -xdelta; var ydelta:Number = randomInRange(0, RIBBON_LENGTH); if (Math.random() < 0.5) ydelta = -ydelta; var zdelta:Number = randomInRange(0, RIBBON_LENGTH); if (Math.random() < 0.5) zdelta = -zdelta; var nextPt:Vertex3D = new Vertex3D(xdelta, ydelta, zdelta); points[points.length] = nextPt; } public function float(min:Number, max:Number = NaN):Number { if (isNaN(max)) { max = min; min = 0; } return Math.random() * (max - min) + min; } public function randomInRange(min:Number, max:Number):int { return int((max - min) * float(2) + min); } public function remove():void { var i:int = curves.length; while( --i > -1 ) { curves[i].remove(); } delete(this); } } import org.papervision3d.core.geom.TriangleMesh3D; import org.papervision3d.core.geom.renderables.Triangle3D; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.math.NumberUV; import org.papervision3d.materials.ColorMaterial; class Curve extends TriangleMesh3D { private var stepId:int; private var width:Number; private var quality:int; private var startPt:Vertex3D; private var endPt:Vertex3D; private var controlPt:Vertex3D; public function Curve( startPt:Vertex3D, endPt:Vertex3D, controlPt:Vertex3D, color:uint, width:Number, quality:int) { this.startPt = startPt; this.endPt = endPt; this.controlPt = controlPt; this.width = width; this.quality = quality; var cm:ColorMaterial = new ColorMaterial(color); cm.doubleSided = cm.tiled = cm.smooth = true; super(cm, [], [], null); } public function addLine():void { var t:Number = stepId / quality; var p0:Vertex3D = getOffsetPoint(t, 0); var p3:Vertex3D = getOffsetPoint(t, width); stepId++; t = stepId / quality; var p1:Vertex3D = getOffsetPoint(t, 0); var p2:Vertex3D = getOffsetPoint(t, width); addLineToMesh(p0, p1, p2, p3); } public function addLineToMesh(aV:Vertex3D, bV:Vertex3D, cV:Vertex3D, dV:Vertex3D):void { var vertices:Array = this.geometry.vertices; var faces:Array = this.geometry.faces; vertices[vertices.length] = aV; vertices[vertices.length] = bV; vertices[vertices.length] = cV; vertices[vertices.length] = dV; var uvA:NumberUV = new NumberUV(0, 0); faces[faces.length] = new Triangle3D(this, [aV, bV, cV], null, [uvA, uvA, uvA]); faces[faces.length] = new Triangle3D(this, [aV, cV, dV], null, [uvA, uvA, uvA]); } public function removeLine():void { this.geometry.vertices.shift(); this.geometry.vertices.shift(); this.geometry.vertices.shift(); this.geometry.vertices.shift(); this.geometry.faces.shift(); this.geometry.faces.shift(); } private function getOffsetPoint( t:Number, k:Number ):Vertex3D { var p0:Vertex3D = startPt; var p1:Vertex3D = controlPt; var p2:Vertex3D = endPt; var xt:Number = ( 1 - t ) * ( 1 - t ) * p0.x + 2 * t * ( 1 - t ) * p1.x + t * t * p2.x; var yt:Number = ( 1 - t ) * ( 1 - t ) * p0.y + 2 * t * ( 1 - t ) * p1.y + t * t * p2.y; var zt:Number = ( 1 - t ) * ( 1 - t ) * p0.z + 2 * t * ( 1 - t ) * p1.z + t * t * p2.z; var xd:Number = t * (p0.x - 2 * p1.x + p2.x) - p0.x + p1.x; var yd:Number = t * (p0.y - 2 * p1.y + p2.y) - p0.y + p1.y; var zd:Number = t * (p0.z - 2 * p1.z + p2.z) - p0.z + p1.z; var dd:Number = Math.pow(xd * xd + yd * yd + zd * zd, .33); return new Vertex3D(xt + ( k * yd ) / dd, yt - ( k * xd ) / dd, zt - ( k * xd ) / dd); } public function remove():void { delete(this); } } PV3D Bezier Ribbons