package com.akjava.gwt.threejsexamples.client.examples.animation.cloth;
import java.util.ArrayList;
import java.util.List;
import com.akjava.gwt.lib.client.JavaScriptUtils;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.gwt.three.client.js.THREE;
import com.akjava.gwt.three.client.js.core.Face3;
import com.akjava.gwt.three.client.js.core.Geometry;
import com.akjava.gwt.three.client.js.extras.geometries.SphereGeometry;
import com.akjava.gwt.three.client.js.math.Vector3;
import com.akjava.gwt.three.client.js.objects.Mesh;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
/**
* @deprecated don't modify here
* @author aki
*
*/
public class Cloth2 {
/*
* Cloth Simulation using a relaxed constrains solver
*/
// Suggested Readings
// Advanced Character Physics by Thomas Jakobsen Character
// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
// http://en.wikipedia.org/wiki/Cloth_modeling
// http://cg.alexandra.dk/tag/spring-mass-system/
// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
double DAMPING = 0.03;
double DRAG = 1 - DAMPING;
double MASS = .1;
double restDistance = 25;
private static int xSegs = 10; //
private static int ySegs = 10; //
//must be javascript function ParametricGeometry use this function
JavaScriptObject clothFunction;
//var cloth = new Cloth(xSegs, ySegs);
double GRAVITY = 981 * 1.4; //
Vector3 gravity = THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(MASS);
double TIMESTEP = 18.0 / 1000;
double TIMESTEP_SQ = TIMESTEP * TIMESTEP;
int[] pins;
boolean wind = true;
double windStrength = 2;
Vector3 windForce = THREE.Vector3(0,0,0);
Vector3 ballPosition = THREE.Vector3(0, -45, 0);
public double ballSize = 60; //40
Vector3 tmpForce = THREE.Vector3();
Double lastTime;
//for ParametricGeometry
public native final JavaScriptObject plane(double width,double height)/*-{
return function(u, v) {
var x = (u-0.5) * width;
var y = (v+0.5) * height;
var z = 0;
return new $wnd.THREE.Vector3(x, y, z);
};
}-*/;
/*}
public ClothPartiicle createParticle(double x,double y,double z,double mass){
ClothPartiicle particle=ClothPartiicle.createObject().cast();
particle.setPosition(doClothFunction(x,y));
return particle;
}
*/
//horible implements
public native final Vector3 doClothFunction(JavaScriptObject clothFunction,double x,double y)/*-{
return clothFunction(x,y);
}-*/;
public class Constrain{
private Particle p1;
private Particle p2;
private double distance;
public Constrain(Particle p1, Particle p2, double distance) {
super();
this.p1 = p1;
this.p2 = p2;
this.distance = distance;
}
}
public class Particle{
Vector3 position;
private Vector3 previous;
private Vector3 original;
//for after modify
public Vector3 getOriginal() {
return original;
}
private Vector3 a;
private double mass;
private double invMass;
private Vector3 tmp;
private Vector3 tmp2;
public Particle(double x,double y,double z,double mass){
position=doClothFunction(clothFunction,x, y);
previous=doClothFunction(clothFunction,x, y);
original=doClothFunction(clothFunction,x, y);
a=THREE.Vector3(0, 0, 0);
this.mass=mass;
this.invMass=1.0 / mass;
tmp=THREE.Vector3(0, 0, 0);
tmp2=THREE.Vector3(0, 0, 0);
}
public void addForce(Vector3 force){
this.a.add(
this.tmp2.copy(force).multiplyScalar(this.invMass)
);
}
public void integrate(double timesq){
Vector3 newPos = this.tmp.subVectors(this.position, this.previous);
newPos.multiplyScalar(DRAG).add(this.position);
newPos.add(this.a.multiplyScalar(timesq));
this.tmp = this.previous;
this.previous = this.position;
this.position = newPos;
this.a.set(0, 0, 0);
}
}
Vector3 diff = THREE.Vector3();
public void satisifyConstrains(Particle p1,Particle p2,double distance) {
diff.subVectors(p2.position, p1.position);
double currentDist = diff.length();
if (currentDist==0) return; // prevents division by 0
Vector3 correction = diff.multiplyScalar(1 - distance/currentDist);
Vector3 correctionHalf = correction.multiplyScalar(0.5);
p1.position.add(correctionHalf);
p2.position.sub(correctionHalf);
}
int w;
int h;
private List<Constrain> constrains=new ArrayList<Constrain>();
public List<Particle> particles=new ArrayList<Particle>();
public Cloth2(){
this(xSegs,ySegs);
}
List<int[]> pinsFormation=new ArrayList<int[]>();
public void initPins(){//this is initialized based on Cloth's xSegs TODO link
int center=w/2+1;
pins= new int[]{center};
pinsFormation.add( pins );
//default all first line
pins = new int[w+1];
for(int i=0;i<=w;i++){
pins[i]=i;
}
pinsFormation.add( pins );
pins = new int[]{ 0 };
pinsFormation.add( pins );
pins = new int[0]; // cut the rope ;)
pinsFormation.add( pins );
pins = new int[]{ 0, w }; // classic 2 pins
pinsFormation.add( pins );
pins = pinsFormation.get(1);
}
public void setPinAll(){
pins=pinsFormation.get(1);
}
//compatible
public Cloth2(int w,int h){
this(w,h,25 * xSegs, 25 * ySegs);
}
public Cloth2(int w,int h,double width,double height){
this.w = w;
this.h = h;
clothFunction=plane(width,height);
restDistance=width/(w);
initPins();
LogUtils.log("restDistance:"+restDistance);
// Create particles
for (int v=0;v<=h;v++) {
for (int u=0;u<=w;u++) {
particles.add(
new Particle((double)u/w*width, (double)v/h*height, 0, MASS)
);
}
}
//double wDistance=width/w;
//double hDistance=height/h;
// Structural
/*
particle exists v<=h & u<=w
*/
for (int v=0;v<h;v++) {
for (int u=0;u<w;u++) {
constrains.add(
new Constrain(particles.get(index(u,v)), particles.get(index(u,v+1)), restDistance)
);
constrains.add(
new Constrain(particles.get(index(u,v)), particles.get(index(u+1,v)), restDistance)
);
}
}
for (int u=w, v=0;v<h;v++) {
constrains.add(
new Constrain(particles.get(index(u,v)), particles.get(index(u,v+1)), restDistance)
);
}
for (int v=h, u=0;u<w;u++) {
constrains.add(
new Constrain(particles.get(index(u,v)), particles.get(index(u+1,v)), restDistance)
);
}
}
private int index(int u,int v){
return u + v * (w + 1);
}
public void simulate(double time,Geometry clothGeometry,Mesh sphereMesh) {
if (lastTime==null) {
lastTime = time;
return;
}
ballPosition.copy(sphereMesh.getPosition());
//var i, il, particles, particle, pt, constrains, constrain;
// Aerodynamics forces
if (wind) {
Face3 face;
JsArray<Face3> faces = clothGeometry.getFaces();
Vector3 normal;
for (int i=0,il=faces.length();i<il;i++) {
face = faces.get(i);
normal = face.getNormal();
tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
particles.get(face.getA()).addForce(tmpForce);
particles.get(face.getB()).addForce(tmpForce);
particles.get(face.getC()).addForce(tmpForce);
}
}
for (int i=0, il = particles.size()
;i<il;i++) {
Particle particle = particles.get(i);
particle.addForce(gravity);
particle.integrate(TIMESTEP_SQ);
}
// Start Constrains
for (int i=0;i<constrains.size();i++) {
Constrain constrain = constrains.get(i);
satisifyConstrains(constrain.p1, constrain.p2, constrain.distance);
}
// Ball Constrains
double now=System.currentTimeMillis();
//ballPosition.setZ(-Math.sin(now/600) * 90 ) ;
//ballPosition.setX(Math.cos(now/400) * 70) ;
//always
//if (sphereMesh.isVisible())
//ballSize=((SphereGeometry)sphereMesh.getGeometry().cast()).getRadius();
for (int i=0;i<particles.size();i++) {
Particle particle = particles.get(i);
Vector3 pos = particle.position;
diff.subVectors(pos, ballPosition);
if (diff.length() < ballSize) {
// collided
diff.normalize().multiplyScalar(ballSize);
pos.copy(ballPosition).add(diff);
}
}
/*
* no floor
// Floor Constains
for (int i=0;i<particles.size();i++) {
Particle particle = particles.get(i);
Vector3 pos = particle.position;
if (pos.getY() < -250) {
pos.setY(-250);
}
}
*/
// Pin Constrains
for (int i=0;i<pins.length;i++) {
int xy = pins[i];
Particle p = particles.get(xy);
p.position.copy(p.original);
p.previous.copy(p.original);
}
}
}