Ok - I grabbed nicoptere's Delaunay library and hacked the Plane class
to accept a GraphicsPath, grab the vertices from it, create Delaunay
triangles and render them.
There are two problems here.
1) nicopteres library doesn't have the concept of a bounding path, so
this solution won't handle convex shapes
2) It is creating too many faces and impacting fairly heavily on
performance
If it wasn't for 2, I might try my hand at fixing 1 - but I think I'm
going to have to use a basic Plane as my floor. It won't follow the
edges of the building nicely, but at least it'll be fast.
Anyway, since I hacked this thing together I thought I'd share.
createFaces() is where it all happens:
package
{
import __AS3__.vec.Vector;
import away3d.arcane;
import away3d.core.base.*;
import away3d.materials.*;
import away3d.primitives.AbstractPrimitive;
import flash.display.GraphicsPath;
import flash.geom.Point;
import net.nicoptere.delaunay.Delaunay;
import net.nicoptere.delaunay.DelaunayTriangle;
use namespace arcane;
/**
* Creates a 3d irregular plane within a GraphicsPath.
*/
public class IrregularPlane extends AbstractPrimitive
{
private var grid:Array;
private var _width:Number;
private var _height:Number;
private var _segmentsW:int;
private var _segmentsH:int;
private var _yUp:Boolean;
private var _path:GraphicsPath;
/**
* @inheritDoc
*/
protected override function buildPrimitive():void
{
super.buildPrimitive();
createFaces();
}
/**
* Defines the width of the plane. Defaults to 100, or the width
of the uv material (if one is applied).
*/
public function get width():Number
{
return _width;
}
public function set width(val:Number):void
{
if (_width == val)
return;
_width = val;
_primitiveDirty = true;
}
/**
* Defines the height of the plane. Defaults to 100, or the
height of the uv material (if one is applied).
*/
public function get height():Number
{
return _height;
}
public function set height(val:Number):void
{
if (_height == val)
return;
_height = val;
_primitiveDirty = true;
}
/**
* Defines the number of horizontal segments that make up the
plane. Defaults to 1.
*/
public function get segmentsW():Number
{
return _segmentsW;
}
public function set segmentsW(val:Number):void
{
if (_segmentsW == val)
return;
_segmentsW = val;
_primitiveDirty = true;
}
/**
* Defines the number of vertical segments that make up the
plane. Defaults to 1.
*/
public function get segmentsH():Number
{
return _segmentsH;
}
public function set segmentsH(val:Number):void
{
if (_segmentsH == val)
return;
_segmentsH = val;
_primitiveDirty = true;
}
/**
* Defines whether the coordinates of the plane points use a yUp
orientation (true) or a zUp orientation (false). Defaults to true.
*/
public function get yUp():Boolean
{
return _yUp;
}
public function set yUp(val:Boolean):void
{
if (_yUp == val)
return;
_yUp = val;
_primitiveDirty = true;
}
/**
* Creates a new <code>Plane</code> object.
*
* @param init [optional] An
initialisation object for specifying
default instance properties.
*/
public function IrregularPlane(path:GraphicsPath, init:Object
= null)
{
super(init);
_path = path;
_width = ini.getNumber("width", 100, {min:0});
_height = ini.getNumber("height", 100, {min:0});
var segments:int = ini.getInt("segments", 1, {min:1});
_segmentsW = ini.getInt("segmentsW", segments, {min:1});
_segmentsH = ini.getInt("segmentsH", segments, {min:1});
_yUp = ini.getBoolean("yUp", true);
if (width*height == 0)
{
if (material is BitmapMaterial)
{
var uvm:BitmapMaterial = material as
BitmapMaterial;
if (width == 0)
width = uvm.width;
if (height == 0)
height = uvm.height;
}
else
{
width = 100;
height = 100;
}
}
type = "IrregularPlane";
url = "primitive";
}
/**
* Returns the vertex object specified by the grid position of
the
mesh.
*
* @param w The horizontal position on the
primitive mesh.
* @param h The vertical position on the primitive
mesh.
*/
public function vertex(w:int, h:int):Vertex
{
if (_primitiveDirty)
updatePrimitive();
return grid[h][w];
}
private function createFaces():void
{
var points:Vector.<Point> = new Vector.<Point>;
var profile:Array = [];
//convert GraphicsPath to Points
for (var i:int = 0; i < _path.commands.length; i++)
{
points.push(new Point(_path.data.shift(),
_path.data.shift()));
}
//Get triangles
var triangle:Vector.<DelaunayTriangle> =
Delaunay.Triangulate(points);
//Add triangles to mesh
for each (var tri:DelaunayTriangle in triangle)
{
var p1: Vertex = new Vertex(tri.p1.x,
0, tri.p1.y);
var p2: Vertex = new Vertex(tri.p2.x,
0, tri.p2.y);
var p3: Vertex = new Vertex(tri.p3.x,
0, tri.p3.y);
trace(p1, p2, p3);
addFace(createFace(p1, p2, p3));
}
}
}
}
On Mar 26, 7:46 pm, wagster <[email protected]> wrote:
> Looks very much like someone else has already done the heavy lifting:
>
> http://en.nicoptere.net/?p=10
>
> I'll grab that and have a play.
>
> On Mar 25, 1:54 pm, Fabrice3D <[email protected]> wrote:
>
> > ah well, once you have the earclipping done, and you need help for the
> > build, just drop me a line.
> > Fabrice
>
> > On Mar 25, 2011, at 1:52 PM, wagster wrote:
>
> > > Thanks Fabrice -
>
> > > There's a fair amount of info out there on Delaunay triangulation
> > > algorithms - I could probably put together a class in AS3 - I'm just
> > > not sure how I'd translate it to a mesh after calculating it. I'll
> > > let you know if I get anywhere with that, but I might just keep
> > > plugging away with SkinExtrude for now and see if I can't make it play
> > > nice!
>
> > > On Mar 25, 11:33 am, Fabrice3D <[email protected]> wrote:
> > >>> ) using SkinExtrude by passing in the path
> > >>> as two halves and joining them up.
>
> > >> until we do not have a delaunay mesh generator (high on my list)
> > >> you simply have to pass your angles for a L and use SkinExtrude
> > >> next issue: Projector class is not ported yet, expect distorts in
> > >> texture.
>
> > >> I'm deep in LatheExtrude atm, which is the most complex extrude tool
> > >> class.
> > >> after that others will follow faster...
>
> > >> Fabrice
>
> > >> On Mar 25, 2011, at 11:01 AM, wagster wrote:
>
> > >>> Ok, I've been looking into this a bit deeper and it seems to be a
> > >>> problem that a few people have come up against before, without a good
> > >>> solution yet. I'm sure there is a solution but I may not understand
> > >>> enough about how 3D rendering works to hit upon it yet.
>
> > >>> Background: I'm working on a 3D floorplan system that allows you to
> > >>> draw the walls as paths and the floors as fills from within Flash and
> > >>> save it as a swf that gets loaded at runtime. The walls are no
> > >>> problem: parse the swf, pull the paths out of a MovieClip called
> > >>> "Walls" and pass them to LinearExtrude to build them vertically.
> > >>> Surprisingly, the floor is more difficult.
>
> > >>> Problem: I get one closed path from a fill in Flash, but I can't
> > >>> figure out how to turn it into a mesh.
>
> > >>> Fabrice suggested (I think) using SkinExtrude by passing in the path
> > >>> as two halves and joining them up. The problem here is that if you
> > >>> have an L-shaped room, the inside corner of the "L" gets filled in as
> > >>> well. This has to work for all types of irregular floor layouts.
> > >>> I've been thinking that essentially what I need is some form of flood-
> > >>> fill algorithm that produces triangles, but I haven't figured out a
> > >>> reliable one myself and traditional flood-fill algorithms are all
> > >>> pixel based.
>
> > >>> Is there a way I could convert all the vertices from the path into the
> > >>> outside edges of a mesh, or rather create mesh by defining the outside
> > >>> edges?
>
> > >>> On Mar 22, 9:18 am, wagster <[email protected]> wrote:
> > >>>> Great. I can just check the number of vertices in the path and double
> > >>>> up the end point if necessary.
>
> > >>>> On Mar 21, 11:37 pm, Fabrice3D <[email protected]> wrote:
>
> > >>>>> I've just uploaded SkinExtrude, if you have an even amount of
> > >>>>> vertices, you should be ok...
> > >>>>> More are coming, but as usual, it happends 1 by 1 :)
> > >>>>> In meanhwile, thats all we can offer...
>
> > >>>>> Fabrice
>
> > >>>>> On Mar 21, 2011, at 11:59 PM, wagster wrote:
>
> > >>>>>> I have a 2D closed path describing a flat shape, like a fill in Flash
> > >>>>>> (that's because that's exactly what it was until I yanked it out of a
> > >>>>>> swf). I need to pass all the vertices into a class that will
> > >>>>>> recreate
> > >>>>>> this fill as a flat 3D object. Could you please tell me which class
> > >>>>>> I
> > >>>>>> should be going for? I thought SkinExtrude was the one, but that
> > >>>>>> seems to require more than one path...
>
> > >>>>>> Oh, and thanks for your previous answer Fabrice - most helpful.