package { /** * @author nicoptere * http://en.nicoptere.net/ * http://fr.nicoptere.net/ */ import flash.display.Bitmap; import flash.events.Event; import flash.events.MouseEvent; import flash.display.Sprite; import sandy.core.Scene3D; import sandy.core.scenegraph.Camera3D; import sandy.core.scenegraph.TransformGroup; import sandy.core.scenegraph.Group; import sandy.core.scenegraph.Shape3D; import sandy.core.scenegraph.Geometry3D; import sandy.primitive.Box; import sandy.materials.WireFrameMaterial; import sandy.materials.ColorMaterial; import sandy.materials.Appearance; import sandy.materials.attributes.LightAttributes; import sandy.materials.attributes.MaterialAttributes; import sandy.core.data.Vertex; //marching cube classes import net.nicoptere.marchingcubes.Triangle; import net.nicoptere.marchingcubes.Vector3D; import net.nicoptere.marchingcubes.Dataset; import net.nicoptere.marchingcubes.MarchingCubes; public class SandyObject extends Sprite { private var scene:Scene3D; private var camera:Camera3D; private var cubeMaterial:WireFrameMaterial; private var shapeMaterial:ColorMaterial; private var cubeAppearance:Appearance; private var meshAppearance:Appearance; public var group:TransformGroup; private var cube:Box; private var mesh:Shape3D; //marching cubes utils private var marchingCubes:MarchingCubes; private var _data:Dataset = null; private var _scale:Number = 50; static private var _instance:SandyObject; //to manipualte the mesh private var mouseDown:Boolean = false; //display the boundingbox private var _boundingBox:Boolean; //display the boundingbox private var _doubleSided:Boolean; //renders the object when drag/rotating private var _liveRender:Boolean = false; //swaps the face normal private var normal:Boolean = true; public function SandyObject( mc:MarchingCubes = null, scale:Number = 30, boundingBox:Boolean = false ) { if ( mc == null ) return; marchingCubes = mc; _data = mc.dataset; _scale = scale; _boundingBox = boundingBox; //for static calls _instance = this; init(); } private function init():void { initSandy(); initMaterials(); initObjects(); initListeners(); } private function initSandy():void { camera = new Camera3D( 500, 500, 60 ); camera.z = -500; scene = new Scene3D( "scene", this, camera, new Group("root") ); //active zone var sp:Sprite = new Sprite (); sp.graphics.beginFill( 0, 0 ); sp.graphics.drawRect( 0, 0, 500, 500 ); addChild( sp ); hitArea = sp; buttonMode = true; } private function initMaterials():void { var materialAttr01:MaterialAttributes = new MaterialAttributes( new LightAttributes( false, 0.3) ); cubeMaterial = new WireFrameMaterial( 2, 0x2F2F2F, 1, materialAttr01); cubeAppearance = new Appearance( cubeMaterial ); var materialAttr02:MaterialAttributes = new MaterialAttributes( new LightAttributes( false, .1 ) ); shapeMaterial = new ColorMaterial( 0xFFCC00, 1, materialAttr02 ); shapeMaterial.lightingEnable = true; meshAppearance = new Appearance( shapeMaterial,shapeMaterial ); } private function initObjects():void { group = new TransformGroup(); scene.root.addChild( group ); cube = new Box( "boundingBox", 1, 1, 1, "quad" ); cube.scaleX = cube.scaleY = cube.scaleZ = _scale; cube.appearance = cubeAppearance; group.addChild( cube ); if ( !_boundingBox ) { cube.visible = false; } } public function build():void { //processes the dataset through the MarchingCubes instance var tris:Array = marchingCubes.compute(); // alternative (static) way of computing the triangles // var tris:Array = MarchingCubes.compute(); if ( tris.length == 0 ) return; // builds up the volume after the newly created triangles //builsds or refreshes the mesh if ( mesh != null) { mesh.geometry = null; group.removeChildByName( 'mesh' ); mesh = null; } var geom:Geometry3D = new Geometry3D(); var vcount:int = 0; var i:int = 0; for ( i = 0; i < tris.length; i++ ) { var t :Triangle = tris[ i ]; var j:int = 0; for ( j = 0; j < 3; j++ ) { geom.setVertex( ( vcount + j ), t.p[ j ].x * _scale, t.p[ j ].y * _scale, t.p[ j ].z * _scale ); } geom.aFacesVertexID[ i ] = [ vcount , vcount + 1, vcount + 2 ]; vcount += 3; } //creates the mesh mesh = new Shape3D( 'mesh', geom, meshAppearance, true ); group.addChild( mesh ); mesh.enableBackFaceCulling = false; //centers the model mesh.setPosition( -( _data.X * _scale ) / 2 , -( _data.Y * _scale ) / 2, -( _data.Z * _scale ) / 2 ); //and eventually rescales the boundingbox if ( _boundingBox ) { cube.scaleX = _data.X * _scale; cube.scaleY = _data.Y * _scale; cube.scaleZ = _data.Z * _scale; } render(); } static public function build():void { _instance.build(); } private function initListeners():void { addEventListener( MouseEvent.MOUSE_DOWN, mouseHandler ); addEventListener( MouseEvent.MOUSE_UP, mouseHandler ); } public function mouseHandler( e:Event ):void { switch( e.type ) { case 'mouseDown': mouseDown = true; //displays only the bounding box when moving to spare resources if ( !_liveRender ) { cube.scaleX = _data.X * _scale; cube.scaleY = _data.Y * _scale; cube.scaleZ = _data.Z * _scale; cube.visible = true; if( mesh !=null )mesh.visible = false; } addEventListener( Event.ENTER_FRAME, onEnterFrame ); break; case 'mouseUp': mouseDown = false; removeEventListener( Event.ENTER_FRAME, onEnterFrame ); //shows the mesh again cube.visible = _boundingBox; if( mesh !=null )mesh.visible = true; render(); break; } } private function resetHandler (e:Event = null):void { reset(); } private function reset ():void { } private function onEnterFrame( e:Event ):void { if ( mouseDown ) { var offx:Number = ( ( stage.stageHeight / 2 - mouseY ) / stage.stageHeight ) * 5; var offy:Number = ( ( stage.stageWidth / 2 - mouseX ) / stage.stageWidth ) * 5; group.rotateX += offx; group.rotateY += offy; } render(); } private function render():void { scene.render( false ); } /** * bounding box visibility */ public function set boundingBox( boolean:Boolean ):void { cube.visible = _boundingBox = boolean; } /** * bounding box visibility */ public function get boundingBox( ):Boolean { return _boundingBox; } /** * renders the mesh when dragging */ public function set liveRender( boolean:Boolean ):void { _liveRender = boolean; } /** * renders the mesh when dragging */ public function get liveRender( ):Boolean { return _liveRender; } /** * sets the model scale */ public function set modelScale( scale:Number ):void { _scale = scale; } /** * returns the model scale */ public function get modelScale():Number { return _scale; } /** * is the mesh material double sided */ public function set doubleSided( boolean:Boolean ):void { mesh.enableBackFaceCulling = _doubleSided = !boolean; } /** * is the mesh material double sided */ public function get doubleSided( ):Boolean { return _doubleSided; } /** * changes the color [ shadow, diffuse ] */ public function set colors( colors:Array ):void { if ( colors == null ) colors = [0x41260f, 0xFFCC00]; var materialAttr02:MaterialAttributes = new MaterialAttributes( new LightAttributes( false, .1 ) ); shapeMaterial = new ColorMaterial( colors[ 0 ], 1, materialAttr02 ); shapeMaterial.lightingEnable = true; meshAppearance = new Appearance( shapeMaterial,shapeMaterial ); } } }