package com.as3dmod.modifiers { import com.as3dmod.IModifier; import com.as3dmod.core.FaceProxy; import com.as3dmod.core.MeshProxy; import com.as3dmod.core.Modifier; import com.as3dmod.core.VertexProxy; import com.as3dmod.core.verlet.VerletConnection; import com.as3dmod.core.verlet.VerletVertex; import com.as3dmod.util.ModConstant; import flash.utils.Dictionary; /** * Cloth modifier. Animated the vertices of a 3D mesh so that it appears to be made out of cloth. *
*
External forces can be applied along the 3 axes, to create effects such as wind or gravity. * Edges of the mesh can be locked, to attach them at a fixed position in space, and bounds can be specified * so the cloth will fold as if resting on the floor or hitting a wall. *
All coordinates are in object space! *
*
Best used with meshes containing flat edges, such as planes or boxes. *
*
The Cloth modifier should be at the top of the modifier stack. * * @author David Lenaerts */ public class Cloth extends Modifier implements IModifier { private var _vertices : Array; private var _connections : Array; private var _forceX : Number = 0; private var _forceY : Number = 0; private var _forceZ : Number = 0; private var _rigidity : Number; private var _friction : Number; private var _lookUp : Dictionary; private var _useBounds : Boolean; private var _boundsMinX : Number; private var _boundsMaxX : Number; private var _boundsMinY : Number; private var _boundsMaxY : Number; private var _boundsMinZ : Number; private var _boundsMaxZ : Number; /** * Creates an instance of the Cloth modifier. * * @param rigidity Determines the rigidity of the cloth. Lower values will make the cloth more stretchable and bouncy. A value between 0 and 1. * @param friction The amount of air friction acting upon the cloth, changing its overall mobility. */ public function Cloth(rigidity : Number = 1, friction : Number = 0) { super(); _lookUp = new Dictionary(true); _rigidity = rigidity; this.friction = friction; } /** * Sets the bounds of the box in which the cloth is contained. This can be used to mimick a floor and/or walls. Coordinates are in object space. * * @param minX The left wall of the bounding box. * @param maxX The right wall of the bounding box. * @param minY The floor of the bounding box. * @param maxY The ceiling of the bounding box. * @param minZ The near wall of the bounding box. * @param maxZ The far wall of the bounding box. */ public function setBounds(minX : Number = Number.NEGATIVE_INFINITY, maxX : Number = Number.POSITIVE_INFINITY, minY : Number = Number.NEGATIVE_INFINITY, maxY : Number = Number.POSITIVE_INFINITY, minZ : Number = Number.NEGATIVE_INFINITY, maxZ : Number = Number.POSITIVE_INFINITY) : void { _useBounds = true; _boundsMinX = minX; _boundsMaxX = maxX; _boundsMinY = minY; _boundsMaxY = maxY; _boundsMinZ = minZ; _boundsMaxZ = maxZ; } /** * Clears all bounds. */ public function clearBounds() : void { _useBounds = false; } /** * The vertices used to calculate the animations. They are different to normal vertices in that they take into account velocity. */ public function get verletVertices() : Array { return _vertices; } /** * The amount of air friction acting upon the cloth, changing its overall mobility. */ public function get friction() : Number { return (_friction-1)*100; } public function set friction(value : Number) : void { if (value < 0) value = 0; _friction = value/100+1; } /** * Determines the rigidity of the cloth. Lower values will make the cloth more stretchable and bouncy. A value between 0 and 1. */ public function get rigidity() : Number { return _rigidity; } public function set rigidity(value : Number) : void { var half : Number; var i : int = _connections.length; var c : VerletConnection; if (value > 1) value = 1; else if (value < 0) value = 0; _rigidity = value; half = value*.5; while (c = _connections[--i] as VerletConnection) { c.rigidity = half; } } /** * Sets the external forces acting on the vertices along the three axes. * This can be used to immitate natural effects such as gravity or wind. * * @param x The amount of force along the X axis. * @param y The amount of force along the Y axis. * @param z The amount of force along the Z axis. */ public function setForce(x : Number, y : Number, z : Number) : void { _forceX = x; _forceY = y; _forceZ = z; } /** * The amount of external forces acting upon the vertices along the X axis. * This can be used to immitate natural effects such as wind. */ public function get forceX() : Number { return _forceX; } public function set forceX(value : Number) : void { _forceX = value; } /** * The amount of external forces acting upon the vertices along the Y axis. * This can be used to immitate natural effects such as gravity. */ public function get forceY() : Number { return _forceY; } public function set forceY(value : Number) : void { _forceY = value; } /** * The amount of external forces acting upon the vertices along the Z axis. * This can be used to immitate natural effects such as wind. */ public function get forceZ() : Number { return _forceZ; } public function set forceZ(value : Number) : void { _forceZ = value; } /** * Removes all locks placed on the Cloth modifier, and sets all vertices to mobile. */ public function unlockAll() : void { var v : VerletVertex; var i : int = _vertices.length; while (v = _vertices[--i] as VerletVertex) { v.mobileX = true; v.mobileY = true; v.mobileZ = true; } } /** * Locks all vertices with a given maximum distance to the left boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. * * @see com.as3dmod.util.ModConstant */ public function lockXMin(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.minX, "x", tolerance, axes); } /** * Locks all vertices with a given maximum distance to the right boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. * * @see com.as3dmod.util.ModConstant */ public function lockXMax(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.maxX, "x", tolerance, axes); } /** * Locks all vertices with a given maximum distance to the bottom boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. * * @see com.as3dmod.util.ModConstant */ public function lockYMin(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.minY, "y", tolerance, axes); } /** * Locks all vertices with a given maximum distance to the top boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. * * @see com.as3dmod.util.ModConstant */ public function lockYMax(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.maxY, "y", tolerance, axes); } /** * Locks all vertices with a given maximum distance to the near boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. */ public function lockZMin(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.minZ, "z", tolerance, axes); } /** * Locks all vertices with a given maximum distance to the far boundary into place, * restricting movement along a set of axes. * * @param tolerance The maximum distance to the boundary for which vertices will be locked. * @param axes The axes along which the movement is restricted. If not provided, no movement is allowed. * * @see com.as3dmod.util.ModConstant */ public function lockZMax(tolerance : Number = 0, axes : int = 7) : void { lockSet(mod.maxZ, "z", tolerance, axes); } private function lockSet(reference : Number, property : String, tolerance : Number = 0, axes : int = 7) : void { var v : VerletVertex; var i : int = _vertices.length; while (v = _vertices[--i] as VerletVertex) { if (Math.abs(v[property]-reference) <= tolerance) { if (axes & ModConstant.X) v.mobileX = false; if (axes & ModConstant.Y) v.mobileY = false; if (axes & ModConstant.Z) v.mobileZ = false; } } } override public function setModifiable(mod:MeshProxy):void { super.setModifiable(mod); initVerletVertices(); initVerletConnections(); rigidity = _rigidity; } public function apply():void { var i : int; var c : VerletConnection; var v : VerletVertex; i = _connections.length; while (c = _connections[--i] as VerletConnection) { c.update(); } i = _vertices.length; while (v = _vertices[--i] as VerletVertex) { if (v.mobileX) v.x += _forceX; if (v.mobileY) v.y += _forceY; if (v.mobileZ) v.z += _forceZ; v.velocityX /= _friction; v.velocityY /= _friction; v.velocityZ /= _friction; if (_useBounds) { if (v.x < _boundsMinX) v.x = _boundsMinX; else if (v.x > _boundsMaxX) v.x = _boundsMaxX; if (v.y < _boundsMinY) v.y = _boundsMinY; else if (v.y > _boundsMaxY) v.y = _boundsMaxY; if (v.z < _boundsMinZ) v.z = _boundsMinZ; else if (v.z > _boundsMaxZ) v.z = _boundsMaxZ; } v.update(); } } private function initVerletVertices() : void { var vs : Array = mod.getVertices(); var vc : int = vs.length; var v : VertexProxy; var vv : VerletVertex; _vertices = []; while (v = vs[--vc] as VertexProxy) { vv = new VerletVertex(v); _vertices.push(vv); _lookUp[v] = vv; } } private function initVerletConnections() : void { var ts : Array = mod.getFaces(); var t : FaceProxy; var tc : int = ts.length; var faceVertices : Array; var numVertices : Number; _connections = []; for (var i : int = 0; i < tc; i++) { t = ts[i] as FaceProxy; faceVertices = t.vertices; numVertices = faceVertices.length; for (var j : int = 0; j < numVertices-1; j++) { createConnection(_lookUp[faceVertices[j]], _lookUp[faceVertices[j+1]]); if (j > 1) createConnection(_lookUp[faceVertices[0]], _lookUp[faceVertices[j]]); } createConnection(_lookUp[faceVertices[numVertices-1]], _lookUp[faceVertices[0]]); } } private function createConnection(v1 : VerletVertex, v2 : VerletVertex) : void { var dist : Number = v1.distanceTo(v2); var connection : VerletConnection = new VerletConnection(v1, v2, dist, _rigidity); _connections.push(connection); } } }