SoftBodyMP.cs
From Unify Community Wiki
SoftBodyMP.cs
/** * SoftBody implementation for Unity v0.2.4-mp (With MidPoint integrator) * (c) Brian R. Cowan, 2007 (http://www.briancowan.net/) * Examples at http://www.briancowan.net/code/ * MidPoint integration adapted from http://www.cs.unc.edu/~coombe/comp259/hw1/prog1/Integrator.cpp * * Code provided as-is. You agree by using this code that I am not liable for any damage * it could possibly cause to you, your machine, or anything else. And the code is not meant * to be used for any medical uses or to run nuclear reactors or robots or such and so. * * Should be easily portable to any other language, all Unity Specific code is labeled so, * adapt it to any other environment. To use, attach the script to an empty object with a * Mesh Renderer and Mesh Filter. Translate it wherever and adjust parameters as desired, * create some spheres that are uniformally scaled, and tag them with a "Softbody" tag or name * them "SphereSoft". Make a plane and name it "PlaneSoft" to use as the yPlane.Dynamic * detection of new collision objects will come in a later version, as well as collisions * against other types of objects. * Some parameters will not affect the cube while the engine * is running. Positioning/Transforming/Rotating the object after initialization will have * strange results. Only one dimension may be set to a resolution of 1, and then the surface * will only show one side of the cloth (I prefer to simulate it with a small width and a 3 * resolution for the this side). * * The Midpoint integrator is meant to be used with a fixed timestep. * * To make the class more editable, The Layout of the file is: * 1) Public Properties (setable in the editor) * 2) Update and Start (what you will be most interested in modifying) * 3) Utility Functions, that interface the code of the algorithm with Unity * 4) Algorithm classes, should have minimal unity dependance * 5) Algorithm variables (protected and private) * 6) Algorithm functions, iterate over the algorithm * 7) Table(s), used to calculate in the algorithm * * Id love to see any project you use the code in. * * Mail any comments to: brian@briancowan.net (Vector426 on freenode.net's #unity3d ) * * Cheers & God bless * * Versions: * v0.2.4-mp Uses spheres with name SoftBody instead of with Tag (Softbody) so it can be * included in a package easier. PlaneSoft yPlane setting. Changed example location * v0.2.3-mp Initial released version for Unity with Midpoint Integrater */ using UnityEngine; using System.Collections; public class SoftBodyMP : MonoBehaviour { //private one sided only private bool oneSided=false; //Total mass of object public float mass=1f; //Properties, the height, width, and length of the SoftBody (currently don't use scale) public Vector3 size=new Vector3(1,1,1); //Properties, the resolution of the mesh in X,Y,Z //For cloth, setting Y to two or three and the others to 16+ gives generally good results public int resolutionX=7; public int resolutionY=7; public int resolutionZ=7; //Public interface to set the YPlane distance to the ground; //Use //public float yPlane=3.0f; //Should the springs between the masses push and pull, or pull only? public bool dTwoWay=true; //Gravity, won't affect much if the masses are already going at the maximum speed public float gravity=.02f; //Velocity magnitude capping, if not set the system will eventually destabalize as it is //currently implemented public float velocityCap=5f; //Default elasticity of connections, lower=stretchier, higher=more rigid public float defaultK=2.5f; //The amount to step every frame, setting to 0 will use deltaTime //(setting to deltaTime can give a wider variation in results every time it is replayed) public float timeStep=.01f; //Unity Start void Start () { //engine specific pre start preStart(); //Algorithm start initialize(); //implementation/engine specific startEngine(); } //Update is called once per frame, try using FixedUpdate void Update () { ftime=(timeStep>0 ? timeStep : Time.deltaTime); //If you want to fix the edge corners (useful for Flags and other bouncy effects) // for(int i=0;i<_lattice.Length;i++) { _lattice[i].fix=true; } /*_lattice[0].fix=true; _lattice[(_dimX-1)*_dimY*(_dimZ)].fix=true; _lattice[_dimX*_dimY*(_dimZ-1)].fix=true; _lattice[(_dimX-1)*_dimY].fix=true; */ //Engine specific updates for mesh preUpdate(); //Not implementation specific perFrame(); //implementation/engine specific renderMesh(); } /*Unity Specific pre algorithm initialization*/ private float lt; //Last time message was displayed //Collision objects private ArrayList allSpheres; private GameObject thePlaneThePlane; void preStart() { //Set internal variables, so if they are changed while in progress nothing will happen //Also make sure that we only have one dimension with a 1 unit marker _dimX=(resolutionX>0 ? resolutionX : 1); _dimY=(resolutionY>1 ? resolutionY : (_dimX > 1 ? 1 : 2)); _dimZ=(resolutionZ>1 ? resolutionZ : (_dimX > 1 && _dimY > 1 ? 1 : 2)); _sX=size.x; _sY=size.y; _sZ=size.z; //Not to be changed... transform.localScale=new Vector3(1,1,1); transform.eulerAngles=new Vector3(0,0,0); //Start time counter at 0 for message lt=0f; //Set onesided if something has a 0 dim vector if(_dimX==1 || _dimY==1 || _dimZ==1) {oneSided=true; } //Add spheres that can be collided against GameObject[] sp2 = GameObject.FindGameObjectsWithTag ("Softbody"); Object[] objs = GameObject.FindObjectsOfType(typeof(GameObject)); allSpheres=new ArrayList(); int cnt=0; for(int i=0;i<sp2.Length;i++) { allSpheres.Add(sp2[i]); cnt++; } for(int i=0;i<objs.Length;i++) { if(objs[i].name=="SphereSoft") { allSpheres.Add(objs[i]); cnt++; } } thePlaneThePlane = GameObject.Find("PlaneSoft"); s=new float[cnt][]; for(int i=0;i<cnt;i++) {s[i]=new float[4];} } /*Unity Specific starting of engine *After the algorithm initiates the mass lattice, this method created the *references from the triangles to the bordering vertices. * */ void startEngine() { int i,j; Mesh mesh=new Mesh(); ((MeshFilter) GetComponent("MeshFilter")).mesh=mesh; int pl=oneSided ? _dimX*_dimY*_dimZ : (_dimX*_dimY+_dimX*_dimZ+_dimY*_dimZ)*2-(_dimX+_dimY+_dimZ); _allP=new Vector3[pl]; _allUV=new Vector2[pl]; _pP=new int[pl]; for(i=0;i<_allP.Length;i++) { _allP[i]=new Vector3(0,0,0); _allUV[i]=new Vector2(0,0); _pP[i]=0; } //How many triangles will there be overall? Assumes at most one dimension with a 1 dim vector int dom; dom=(_dimX>1 ? (_dimY>1 ? (_dimZ>1 ? (((_dimX-1)*(_dimY-1)) + ((_dimX-1)*(_dimZ-1)) + ((_dimZ-1)*(_dimY-1)))*2 : ((_dimX-1)*(_dimY-1))*2) : ((_dimX-1)*(_dimZ-1))*2) : ((_dimY-1)*(_dimZ-1))*2); _allT=new int[3*dom*2]; for(i=0;i<_allT.Length;i++) { _allT[i]=0; } int pc=0; //Go through all the points, and assign their positions in vertex buffer, and UV coordinates for(i=0;i<_lattice.Length;i++) { Vector3 p=_lattice[i].position; float ux=(float)_lattice[i].px/(float)_dimX; float uy=(float)_lattice[i].py/(float)_dimY; float uz=(float)_lattice[i].pz/(float)_dimZ; if(_lattice[i].pz>0 && _lattice[i].pz<_dimZ-1) { if(_lattice[i].px>0 && _lattice[i].px<_dimX-1) { if(_lattice[i].py>0 && _lattice[i].py<_dimY-1) { //Not a bordering point _lattice[i].pP=0; } else { _lattice[i].pP=pc; _allP[pc]=new Vector3(p.x,p.y,p.z); _allUV[pc]=new Vector2(ux,uz); _pP[pc++]=i; } } else { _lattice[i].pP=pc; _allP[pc]=new Vector3(p.x,p.y,p.z); _allUV[pc]=new Vector2(uy,uz); _pP[pc++]=i; } } else { _lattice[i].pP=pc; _allP[pc]=new Vector3(p.x,p.y,p.z); _allUV[pc]=new Vector2(ux,uy); _pP[pc++]=i; } } //Go through faces of cube and connect the triangles to the points in the Vertex buffer int tp=0; int ymul=_dimX; int zmul=_dimX*_dimY; int addt=0; if(_dimY>1 && _dimX>1) { if(!oneSided){ for(i=0;i<_dimY-1;i++) { for(j=0;j<_dimX-1;j++) { _allT[tp++]=_lattice[i*ymul+j].pP; _allT[tp++]=_lattice[(i+1)*ymul+j].pP; _allT[tp++]=_lattice[i*ymul+j+1].pP; _allT[tp++]=_lattice[i*ymul+j+1].pP;_allT[tp++]=_lattice[(i+1)*ymul+j].pP; _allT[tp++]=_lattice[(i+1)*ymul+j+1].pP; } } } addt=(_dimZ-1)*_dimY*_dimX; for(i=0;i<_dimY-1;i++) { for(j=0;j<_dimX-1;j++) { _allT[tp++]=_lattice[i*ymul+j+addt].pP; _allT[tp++]=_lattice[i*ymul+j+1+addt].pP; _allT[tp++]=_lattice[(i+1)*ymul+j+addt].pP; _allT[tp++]=_lattice[i*ymul+j+1+addt].pP;_allT[tp++]=_lattice[(i+1)*ymul+j+1+addt].pP; _allT[tp++]=_lattice[(i+1)*ymul+j+addt].pP; } } } //Sides if(_dimZ>1) { if(_dimX>1) { //z&x if(!oneSided){ for(i=0;i<_dimZ-1;i++) { for(j=0;j<_dimX-1;j++) { _allT[tp++]=_lattice[i*zmul+j].pP; _allT[tp++]=_lattice[i*zmul+j+1].pP;_allT[tp++]=_lattice[(i+1)*zmul+j].pP; _allT[tp++]=_lattice[i*zmul+j+1].pP;_allT[tp++]=_lattice[(i+1)*zmul+j+1].pP;_allT[tp++]=_lattice[(i+1)*zmul+j].pP; } } } addt=(_dimX)*(_dimY-1); for(i=0;i<_dimZ-1;i++) { for(j=0;j<_dimX-1;j++) { _allT[tp++]=_lattice[i*zmul+j+addt].pP; _allT[tp++]=_lattice[(i+1)*zmul+j+addt].pP;_allT[tp++]=_lattice[i*zmul+j+1+addt].pP; _allT[tp++]=_lattice[i*zmul+j+1+addt].pP;_allT[tp++]=_lattice[(i+1)*zmul+j+addt].pP;_allT[tp++]=_lattice[(i+1)*zmul+j+1+addt].pP; } } } if(_dimY>1){ //y&x if(!oneSided){ for(i=0;i<_dimZ-1;i++) { for(j=0;j<_dimY-1;j++) { _allT[tp++]=_lattice[i*zmul+j*ymul].pP; _allT[tp++]=_lattice[(i+1)*zmul+j*ymul].pP;_allT[tp++]=_lattice[i*zmul+(j+1)*ymul].pP; _allT[tp++]=_lattice[i*zmul+(j+1)*ymul].pP; _allT[tp++]=_lattice[(i+1)*zmul+j*ymul].pP;_allT[tp++]=_lattice[(i+1)*zmul+(j+1)*ymul].pP; } } } addt=(_dimX-1); for(i=0;i<_dimZ-1;i++) { for(j=0;j<_dimY-1;j++) { _allT[tp++]=_lattice[i*zmul+j*ymul+addt].pP;_allT[tp++]=_lattice[i*zmul+(j+1)*ymul+addt].pP; _allT[tp++]=_lattice[(i+1)*zmul+j*ymul+addt].pP; _allT[tp++]=_lattice[i*zmul+(j+1)*ymul+addt].pP;_allT[tp++]=_lattice[(i+1)*zmul+(j+1)*ymul+addt].pP;_allT[tp++]=_lattice[(i+1)*zmul+j*ymul+addt].pP; } } } } //Assign to unity mesh mesh.vertices=_allP; mesh.uv=_allUV; mesh.triangles=_allT; } /*Unity Specific positioning of Collision Objects *Currently only spheres and yPlane. Will inverse transform their positions, *it is set in such a way that Spheres may be moved/removed/added every frame. */ void preUpdate() { int i=0; foreach (GameObject cs in allSpheres) { Vector3 ip=transform.InverseTransformPoint(cs.transform.position); s[i][0]=ip.x; s[i][1]=ip.y; s[i][2]=ip.z; s[i][3]=cs.transform.lossyScale.x*.5f+.01f; i++; } _yFix=thePlaneThePlane.transform.position.y-transform.position.y; } /*Unity and Sample Specific *Code to update the displayed mesh with the calculated lattice */ private void renderMesh() { int i; Mesh mesh=((MeshFilter) GetComponent("MeshFilter")).mesh; for(i=0;i<_allP.Length;i++) { _allP[i]=_lattice[_pP[i]].position; } mesh.vertices = _allP;//fv ; mesh.RecalculateNormals(); mesh.RecalculateBounds(); // if a message is desired to be displayed once every second /*if(lt+1<Time.time) { lt=Time.time; string str="T:"+_allT.Length/3+" V:"+_allP.Length+" L:"+_lattice[0].force+" FPS:"+(int)(1f/Time.deltaTime); Debug.Log(Time.time+" - "+str); }*/ } //Class for one mass point in the soft body public class cMass { //One Connection to another mass public class cmConnect { //The original and connected to masses //This is done so the connected to mass can be updated with the inverse of the //calculated force without recalculating it when it is revisited public cMass cm; public cMass alter; //Rest length of connection public float length; //Springyness public float k; //Two way? (For jello/pillow/folds in cloth) public bool twoWay; //axis id of hinge public int axis; //alternate (reversed) axis of hinge public int alterAxis; //Last frame connection calculated at (if it is calculated from the reverse connection, then don't recalc) public int lptr,lptr_h; //Calculates the connection variables public cmConnect(cMass cm,int axis,cMass alter) { this.lptr=0; this.lptr_h=0; this.cm=cm; this.alter=alter; //Use Softbody defaults at first k=cm.myBody.defaultK; twoWay=cm.myBody.dTwoWay; this.axis=axis; //Calculate the rest lengths in x/y/z to the next mass float dx=(cm.myBody._dimX>1 ? cm.myBody._sX/((float)cm.myBody._dimX-1f) : 0); float dy=(cm.myBody._dimY>1 ? cm.myBody._sY/((float)cm.myBody._dimY-1f) : 0); float dz=(cm.myBody._dimZ>1 ? cm.myBody._sZ/((float)cm.myBody._dimZ-1f) : 0); //The alternate axis is always the total amount of axis-this axis this.alterAxis=(SoftBodyMP.maxCon-1)-axis; //Calculate the length, taking the square of the steps in x,y,z in that axis int[,] aa=SoftBodyMP.allAxis; this.length=Mathf.Sqrt( dx*dx*(aa[axis,0]*aa[axis,0]) + dy*dy*(aa[axis,1]*aa[axis,1]) + dz*dz*(aa[axis,2]*aa[axis,2])); } //Calculate the force that is exerted by this spring on both the connected masses //Uses a general Newtonian formula f=kd public void addForce() { this.lptr=cm.myBody.fptr; Vector3 dir=alter.position_h-cm.position_h; float tLength=dir.magnitude; float tDelta=(tLength-this.length); //Only calculate if the spring is a two way, or stretched beyond rest length if(tDelta>0 || twoWay) { float forcem=(k*(tDelta)); Vector3 nForce=dir*forcem; cm.force=cm.force+nForce; cmConnect acmC=alter._cons[alterAxis]; //add inverse to alternate mass if it hasn't been calculated through this axis //if(acmC==null) {Debug.Log(axis+"&"+alterAxis+" - "+cm.px+","+cm.py+","+cm.pz);} Debug purposes if( acmC.lptr<this.lptr) { acmC.lptr=this.lptr; alter.force=alter.force-nForce; } } else{ //set inverse as calculated, even though nothing was added cmConnect acmC=alter._cons[alterAxis]; if( acmC.lptr<this.lptr) { acmC.lptr=this.lptr; } } } //Calculate the force that is exerted by this spring on both the connected masses //Uses a general Newtonian formula f=kd public void addForce_h() { this.lptr_h=cm.myBody.fptr; Vector3 dir=alter.position-cm.position; float tLength=dir.magnitude; float tDelta=(tLength-this.length); //Only calculate if the spring is a two way, or stretched beyond rest length if(tDelta>0 || twoWay) { float forcem=(k*(tDelta)); Vector3 nForce=dir*forcem; cm.force_h=cm.force_h+nForce; cmConnect acmC=alter._cons[alterAxis]; //add inverse to alternate mass if it hasn't been calculated through this axis //if(acmC==null) {Debug.Log(axis+"&"+alterAxis+" - "+cm.px+","+cm.py+","+cm.pz);} Debug purposes if( acmC.lptr_h<this.lptr_h) { acmC.lptr_h=this.lptr_h; alter.force_h=alter.force_h-nForce; } } else{ //set inverse as calculated, even though nothing was added cmConnect acmC=alter._cons[alterAxis]; if( acmC.lptr_h<this.lptr_h) { acmC.lptr_h=this.lptr_h; } } } } //SoftBody this mass belongs to SoftBodyMP myBody; //discrete coordinates in the lattice public int px; public int py; public int pz; //Newtonian variables in threespace public Vector3 force; public Vector3 force_h; public Vector3 velocity; public Vector3 velocity_h; public Vector3 position; public Vector3 position_h; //Pointer into the displayed triangle list public int pP; //Is it fixed? (unmoving) public bool fix; //All the connections to other masses (Refer to SoftBody.allAxis to see their relation) public cmConnect[] _cons; //Initialize the mass, its starting position and forces, and allocate for Connections public cMass(SoftBodyMP myBody, int px,int py, int pz) { float vx=0;float vy=0;float vz=0; if(myBody._dimX>1) { vx=-(myBody._sX*.5f)+((myBody._sX/((float)myBody._dimX-1f))*((float)px)); } if(myBody._dimY>1) { vy=-(myBody._sY*.5f)+((myBody._sY/((float)myBody._dimY-1f))*((float)py)); } if(myBody._dimZ>1) { vz=-(myBody._sZ*.5f)+((myBody._sZ/((float)myBody._dimZ-1f))*((float)pz)); } force=new Vector3(0,0,0); velocity=new Vector3(0,0,0); position=new Vector3(vx,vy,vz); velocity_h=new Vector3(0,0,0); force_h=new Vector3(0,0,0); position_h=new Vector3(0,0,0); this.px=px; this.py=py; this.pz=pz; this.myBody=myBody; this.fix=false; _cons=new cmConnect[SoftBodyMP.maxCon]; } //Calculate the forces across all the connections to this object, but only connections that haven't already //been calculated for (due to inverse axis) public void calcCons() { for(int j=0;j<SoftBodyMP.maxCon;j++) { cmConnect c=this._cons[j]; if(c!=null && c.lptr<myBody.fptr) { c.addForce(); } } } public void calcCons_h() { for(int j=0;j<SoftBodyMP.maxCon;j++) { cmConnect c=this._cons[j]; if(c!=null && c.lptr<myBody.fptr) { c.addForce_h(); } } } //Once forces have been calculated, add forces to velocity, cap it, and add velocity to position, then check collisions public void doMovement_h() { if(!this.fix) { float t=this.myBody.ftime; //If there are more masses, each one would weigh less so force would be more effective this.velocity_h=this.velocity+((t*.5f)*this.force_h*this.myBody._mEach); if(this.velocity_h.magnitude>myBody.velocityCap) { this.velocity_h=this.velocity_h.normalized*myBody.velocityCap; } this.position_h=this.position+((t*.5f)*this.velocity); //this.velocity=this.velocity+(this.velocity_h-this.velocity)*2; //No mega numeric instability explosions! //this.position=this.position+(this.position_h-this.position)*2; doCollision_h(); } } //Once forces have been calculated, add forces to velocity, cap it, and add velocity to position, then check collisions public void doMovement() { if(!this.fix) { float t=this.myBody.ftime; //If there are more masses, each one would weigh less so force would be more effective this.velocity=this.velocity+(this.force*this.myBody._mEach*t);//+this.force*this.myBody._mEach)); //this.velocity=this.velocity+(this.velocity_h-this.velocity)*2; //No mega numeric instability explosions! if(this.velocity.magnitude>myBody.velocityCap) { this.velocity=this.velocity.normalized*myBody.velocityCap; } this.position=this.position+(this.velocity+this.velocity_h*.5f)*t;//+this.velocity_h*.5f)*t; //this.position=this.position+(this.position_h-this.position)*2; //check collisions, which will move the mass if collided doCollision(); } } //Collision interaction public void doCollision() { float[][] s=myBody.s; //zfloor check, takes in account friction and floor bouncyness if(this.position.y<myBody._yFix) { this.position.y=myBody._yFix; this.velocity.x=this.velocity.x*myBody.dFriction; this.velocity.z=this.velocity.z*myBody.dFriction; this.velocity.y=-this.velocity.y*myBody.dCaucho; } //Sphere collisions, inexact but they work for(int i=0;i<s.Length;i++) { Vector3 c=new Vector3(s[i][0],s[i][1],s[i][2]); //Vector3 op=this.position; Vector3 d=this.position-c; float r=s[i][3]; float dst=d.magnitude; if(dst<r) { Vector3 u=d.normalized; float dot=Vector3.Dot(this.velocity.normalized,u); float fric=(myBody.dFriction*this.force.magnitude*dot); this.velocity=((1f-dot)*this.velocity*fric)+(dot*d.normalized*this.velocity.magnitude)*myBody.dCaucho; this.position=c+(u*r+this.velocity*(r-dst)); } } } //Collision interaction public void doCollision_h() { float[][] s=myBody.s; //zfloor check, takes in account friction and floor bouncyness if(this.position_h.y<myBody._yFix) { this.position_h.y=myBody._yFix; this.velocity_h.x=this.velocity_h.x*myBody.dFriction; this.velocity_h.z=this.velocity_h.z*myBody.dFriction; this.velocity_h.y=-this.velocity_h.y*myBody.dCaucho; } //Sphere collisions, inexact but they work for(int i=0;i<s.Length;i++) { Vector3 c=new Vector3(s[i][0],s[i][1],s[i][2]); //Vector3 op=this.position; Vector3 d=this.position_h-c; float r=s[i][3]; float dst=d.magnitude; if(dst<r) { Vector3 u=d.normalized; float dot=Vector3.Dot(this.velocity_h.normalized,u); float fric=(myBody.dFriction*this.force_h.magnitude*dot); this.velocity_h=((1f-dot)*this.velocity_h*fric)+(dot*d.normalized*this.velocity_h.magnitude)*myBody.dCaucho; this.position_h=c+(u*r+this.velocity_h*(r-dst)); } } } //Initiate all the connections for this mass public int makeLattice() { cMass tm; int count=0; int i; int[,] aa=SoftBodyMP.allAxis; for(i=0;i<SoftBodyMP.maxCon;i++) { tm=myBody.findMass(px+aa[i,0],py+aa[i,1],pz+aa[i,2]); if(tm!=null) {_cons[i]=new cmConnect(this,i,tm); count++; } else {_cons[i]=null;} } return count; } } //This is the lattice of masses that make up the Soft Body private cMass[] _lattice; //Internal: Dimensions of connections protected int _dimX,_dimY,_dimZ; //frame pointer, to see if a connection has already been calculated for protected int fptr; //Distance in time to calculate for protected float ftime; //Internal Distance to ground, calculated every frame from transform protected float _yFix; //Default Springyness damper/amplifier against hard surfaces protected float dCaucho=.3f; //Default Friction against hard surfaces protected float dFriction=.2f; //Internal: The size in X and Y and Z protected float _sX,_sY,_sZ; //To use to multiply force by (amount of masses/total mass) protected float _mEach; //Spheres that will be calculated for protected float[][] s; //All the points, UVs, Triangles, and reverse lookup of points protected Vector3[] _allP; protected Vector2[] _allUV; protected int[] _allT; protected int[] _pP; //Give coordinates into lattice, find a mass protected cMass findMass(int x,int y,int z) { if(x<0 || y<0 || z<0 || x>=_dimX || y>=_dimY || z>=_dimZ) { return null; } else {return _lattice[z*_dimX*_dimY + y*_dimX + x];} } //Every frame, clear the forces, and add gravity otherwise things may become a little unstable... protected void clearForces() { for(int i=0;i<_lattice.Length;i++) { //For Windy looking thing //_lattice[i].force.x=.0006f+Mathf.Sin(Time.time)*.0002f;_lattice[i].force.y=-gravity;_lattice[i].force.z=Mathf.Sin(Time.time+_lattice[i].position.x*2)*.0008f; _lattice[i].force.x=0f;_lattice[i].force.y=-gravity;_lattice[i].force.z=0f; _lattice[i].force_h.x=0f;_lattice[i].force_h.y=-gravity;_lattice[i].force_h.z=0f; } } protected void copyForces() { for(int i=0;i<_lattice.Length;i++) { //For Windy looking thing //_lattice[i].force.x=.0006f+Mathf.Sin(Time.time)*.0002f;_lattice[i].force.y=-gravity;_lattice[i].force.z=Mathf.Sin(Time.time+_lattice[i].position.x*2)*.0008f; //_lattice[i].force=_lattice[i].force_h; _lattice[i].force.x=0f;_lattice[i].force.y=-gravity;_lattice[i].force.z=0f; _lattice[i].force_h.x=0f;_lattice[i].force_h.y=-gravity;_lattice[i].force_h.z=0f; } } //Algorithm specific per-frame calculations void perFrame() { fptr++; clearForces(); for(int i=0;i<_lattice.Length;i++) { cMass ptr=_lattice[i]; ptr.calcCons_h(); //calculate addition of all the forces ptr.doMovement_h(); //move according to time and calculated force, includes collision detection } // copyForces(); for(int i=0;i<_lattice.Length;i++) { cMass ptr=_lattice[i]; ptr.calcCons(); //calculate addition of all the forces ptr.doMovement(); //move according to time and calculated force, includes collision detection } } //Initialize algorithm, including the mass lattice void initialize() { int jx,jy,jz; //float vx,vy,vz; int i; int massCount=_dimX*_dimY*_dimZ; //How much to multiply forces by _mEach=(_dimX*_dimY*_dimZ)/mass; //Initialize frame counter to 0 fptr=0; _lattice=new cMass[massCount]; i=0; for(jz=0;jz<_dimZ;jz++) { for(jy=0;jy<_dimY;jy++) { for(jx=0;jx<_dimX;jx++) { _lattice[i++]=new cMass(this,jx,jy,jz); } } } i=0; while(i<massCount) { _lattice[i++].makeLattice(); } } //The connection axis, each row represents the relative distance //in x,y,z units of the mass connected via the connection. The inverse //axis can be calculated by maxCon-index. static public int maxCon=74; static protected int[,] allAxis=new int[,] {{0,+1,-2}, {0,+1,+2}, {0,+2,-2}, {0,+2,+2}, {0,+2,-1}, {0,+2,+1}, {+1,0,-2}, {+1,0,+2}, {+2,0,-2}, {+2,0,+2}, {+2,0,-1}, {+2,0,+1}, {-2,-2,-2}, {-2,+2,-2}, {+2,-2,-2}, {+2,+2,-2}, {-1,-1,-1}, {-1,+1,-1}, {+1,-1,-1}, {+1,+1,-1}, {-1,0,-1}, {+1,0,-1}, {0,-1,-1}, {0,+1,-1}, {-2,-2,0}, {+2,-2,0}, {+1,-2,0}, {-1,-2,0}, {-2,-1,0}, {-2,+1,0}, {0,-2,0}, {-2,0,0}, {-1,-1,0}, {+1,-1,0}, {0,0,-1}, {0,-1,0}, {-1,0,0}, {+1,0,0}, {0,+1,0}, {0,0,+1}, {-1,+1,0}, {+1,+1,0}, {+2,0,0}, {0,+2,0}, {+2,-1,0}, {+2,+1,0}, {+1,+2,0}, {-1,+2,0}, {-2,+2,0}, {+2,+2,0}, {0,-1,+1}, {0,+1,+1}, {-1,0,+1}, {+1,0,+1}, {-1,-1,+1}, {-1,+1,+1}, {+1,-1,+1}, {+1,+1,+1}, {-2,-2,+2}, {-2,+2,+2}, {+2,-2,+2}, {+2,+2,+2}, {-2,0,-1}, {-2,0,+1}, {-2,0,-2}, {-2,0,+2}, {-1,0,-2}, {-1,0,+2}, {0,-2,-1}, {0,-2,+1}, {0,-2,-2}, {0,-2,+2}, {0,-1,-2}, {0,-1,+2}}; }