public void reset(int index) {
    if (systems == null) {
      systems = new ParticleSystem[maxStep];
      steps = new int[maxStep];
      addVelocities = new Vector3[maxStep][particleSize];
    }
    if (systems[index] == null) { // initialize
      Geometry geometry = THREE.Geometry();
      for (int i = 0; i < particleSize; i++) {
        geometry.vertices().push(THREE.Vector3(0, 0, 0));
      }

      ParticleSystem system = THREE.ParticleSystem(geometry, baseMaterial.build());
      // system.setSortParticles(true);
      systems[index] = system;
      parent.add(system);

    } else {
      //

      ParticleSystem system = systems[index];
      ((ParticleBasicMaterial) system.getMaterial()).setSize(baseSize);

      for (int i = 0; i < particleSize; i++) {
        Vector3 vertex = system.getGeometry().vertices().get(i);
        vertex.set(0, 0, 0);
      }
      system.getGeometry().setVerticesNeedUpdate(true);
    }
    // addVelocities=new Vector3[maxStep][particleSize];
    // simple and boring
    for (int i = 0; i < particleSize; i++) {
      addVelocities[index][i] =
          THREE.Vector3(
              plusMinus(velocityRange.getX()),
              plusMinus(velocityRange.getY()),
              plusMinus(velocityRange.getZ()));
    }

    Vector3 addwind =
        THREE.Vector3(
            plusMinus(velocityRange.getX()) * 2,
            plusMinus(velocityRange.getY()) * 2,
            plusMinus(velocityRange.getZ()) * 2);
    // wind.addSelf(addwind);
    if (index % 5 == 0) {
      wind.set(
          wind.getX() / 4 * 3 + addwind.getX() / 4,
          wind.getY() / 4 * 3 + addwind.getY() / 4,
          wind.getZ() / 4 * 3 + addwind.getZ() / 4);
    }
    for (int i = 0; i < particleSize; i++) {
      winds[i] =
          THREE.Vector3(
              plusMinus(velocityRange.getX()) * 5,
              plusMinus(velocityRange.getY()) * 5,
              plusMinus(velocityRange.getZ()) * 5);
    }
  }
  public void update() {

    if (systems == null || systems[index] == null) {
      reset(index);
    }

    for (int j = 0; j < maxStep; j++) {
      ParticleSystem system = systems[j];
      if (system == null) {
        break;
      }
      Vector3 addAccel = THREE.Vector3(0, accel.getY() * steps[j], 0);
      for (int i = 0; i < particleSize; i++) {
        Vector3 vertex = system.getGeometry().vertices().get(i);

        vertex.addSelf(velocity);
        vertex.addSelf(addVelocities[j][i]);
        vertex.addSelf(wind);
        vertex.addSelf(addAccel); // do slow

        // vertex.getPosition().addSelf(winds[i]);
      }
      system.getGeometry().setVerticesNeedUpdate(true);

      ((ParticleBasicMaterial) system.getMaterial()).setSize(baseSize + changeSize * steps[j]);
      ((ParticleBasicMaterial) system.getMaterial())
          .setOpacity(Math.max(0, 1 - changeOpacity * steps[j]));

      steps[j]++;
      if (steps[j] == maxStep) {
        reset(j);
        steps[j] = 0;
      }
    }

    index++;
    if (index == maxStep) {
      index = 0;
    }
  }
/**
 * TODO time base
 *
 * @author aki
 */
public class EmitterSystem {
  private Object3D parent;

  public Object3D getParent() {
    return parent;
  }

  public void setParent(Object3D parent) {
    this.parent = parent;
  }

  private int maxStep = 120;

  private Vector3 velocityRange;

  public Vector3 getVelocityRange() {
    return velocityRange;
  }

  public void setVelocityRange(Vector3 velocityRange) {
    this.velocityRange = velocityRange;
  }

  public Vector3 getVelocity() {
    return velocity;
  }

  public void setVelocity(Vector3 velocity) {
    this.velocity = velocity;
  }

  private Vector3 velocity;

  private int particleSize = 1;

  private double baseSize = 50;

  /*
  private ParticleBasicMaterialBuilder baseMaterial=THREE.ParticleBasicMaterial().transparent(true).sizeAttenuation(false).
  	depthTest(false).size(baseSize).blending(THREE.AdditiveBlending).map(ImageUtils.loadTexture("img/particles2.png"));//
  */
  private ParticleBasicMaterialBuilder baseMaterial =
      THREE.ParticleBasicMaterial()
          .transparent(true)
          .depthTest(false)
          .blending(THREE.Blending.NormalBlending())
          .color(0x666666)
          .size(baseSize)
          .map(ImageUtils.loadTexture("img/particle4.png")); //

  Vector3[][] addVelocities;

  Vector3 wind = THREE.Vector3(0, 0, 0);

  Vector3 accel = THREE.Vector3(0, -0.5 / 120, 0);
  // Vector3 accel=THREE.Vector3(0,-2.8/120,0); //spring

  private double changeOpacity = 1.0 / 120;
  private double changeSize = 150.0 / 120;
  ParticleSystem[] systems;
  int steps[];
  int index;
  Vector3 winds[] = new Vector3[particleSize];

  public void reset(int index) {
    if (systems == null) {
      systems = new ParticleSystem[maxStep];
      steps = new int[maxStep];
      addVelocities = new Vector3[maxStep][particleSize];
    }
    if (systems[index] == null) { // initialize
      Geometry geometry = THREE.Geometry();
      for (int i = 0; i < particleSize; i++) {
        geometry.vertices().push(THREE.Vector3(0, 0, 0));
      }

      ParticleSystem system = THREE.ParticleSystem(geometry, baseMaterial.build());
      // system.setSortParticles(true);
      systems[index] = system;
      parent.add(system);

    } else {
      //

      ParticleSystem system = systems[index];
      ((ParticleBasicMaterial) system.getMaterial()).setSize(baseSize);

      for (int i = 0; i < particleSize; i++) {
        Vector3 vertex = system.getGeometry().vertices().get(i);
        vertex.set(0, 0, 0);
      }
      system.getGeometry().setVerticesNeedUpdate(true);
    }
    // addVelocities=new Vector3[maxStep][particleSize];
    // simple and boring
    for (int i = 0; i < particleSize; i++) {
      addVelocities[index][i] =
          THREE.Vector3(
              plusMinus(velocityRange.getX()),
              plusMinus(velocityRange.getY()),
              plusMinus(velocityRange.getZ()));
    }

    Vector3 addwind =
        THREE.Vector3(
            plusMinus(velocityRange.getX()) * 2,
            plusMinus(velocityRange.getY()) * 2,
            plusMinus(velocityRange.getZ()) * 2);
    // wind.addSelf(addwind);
    if (index % 5 == 0) {
      wind.set(
          wind.getX() / 4 * 3 + addwind.getX() / 4,
          wind.getY() / 4 * 3 + addwind.getY() / 4,
          wind.getZ() / 4 * 3 + addwind.getZ() / 4);
    }
    for (int i = 0; i < particleSize; i++) {
      winds[i] =
          THREE.Vector3(
              plusMinus(velocityRange.getX()) * 5,
              plusMinus(velocityRange.getY()) * 5,
              plusMinus(velocityRange.getZ()) * 5);
    }
  }

  private double plusMinus(double value) {
    return value * 2 * Math.random() - value;
  }

  public void update() {

    if (systems == null || systems[index] == null) {
      reset(index);
    }

    for (int j = 0; j < maxStep; j++) {
      ParticleSystem system = systems[j];
      if (system == null) {
        break;
      }
      Vector3 addAccel = THREE.Vector3(0, accel.getY() * steps[j], 0);
      for (int i = 0; i < particleSize; i++) {
        Vector3 vertex = system.getGeometry().vertices().get(i);

        vertex.addSelf(velocity);
        vertex.addSelf(addVelocities[j][i]);
        vertex.addSelf(wind);
        vertex.addSelf(addAccel); // do slow

        // vertex.getPosition().addSelf(winds[i]);
      }
      system.getGeometry().setVerticesNeedUpdate(true);

      ((ParticleBasicMaterial) system.getMaterial()).setSize(baseSize + changeSize * steps[j]);
      ((ParticleBasicMaterial) system.getMaterial())
          .setOpacity(Math.max(0, 1 - changeOpacity * steps[j]));

      steps[j]++;
      if (steps[j] == maxStep) {
        reset(j);
        steps[j] = 0;
      }
    }

    index++;
    if (index == maxStep) {
      index = 0;
    }
  }
}
  /*
   *** sometime it error happend,check
   * double mouseMoved=(Math.abs(diffX)+Math.abs(diffY));
   *double length=newPos.clone().subSelf(getCurrentIkData().getTargetPos()).length();
   *  if(length<mouseMoved*5){
   */
  public Vector3 moveSelectionPosition(
      int mouseX, int mouseY, int screenWidth, int screenHeight, Camera camera) {
    if (isSelected()) {
      try {
        Ray ray = projector.gwtCreateRay(mouseX, mouseY, screenWidth, screenHeight, camera);
        JsArray<Intersect> intersects = ray.intersectObject(mouseCatchPlane);

        Vector3 newPos = intersects.get(0).getPoint().subSelf(draggableOffset);
        log = "newPos-raw:" + ThreeLog.get(newPos);
        Vector3 parentPos = THREE.Vector3();
        if (selectedDraggablekObject.getParent() != null) {
          // selectedDraggablekObject.getParent().updateMatrixWorld(true); //if call it bone moving
          // will be broken.i have no idea.
          parentPos =
              GWTThreeUtils.toPositionVec(selectedDraggablekObject.getParent().getMatrixWorld());
          // LogUtils.log("parent:"+ThreeLog.get(parentPos));
          log += "parentPos:" + ThreeLog.get(parentPos);
        }
        newPos.subSelf(parentPos);

        Matrix4 rotM = THREE.Matrix4();
        Vector3 rotation =
            GWTThreeUtils.rotationToVector3(
                (selectedDraggablekObject.getParent().getMatrixWorld()));
        // rotM.getInverse(selectedDraggablekObject.getMatrixRotationWorld());

        log += "parentRot:" + ThreeLog.get(GWTThreeUtils.radiantToDegree(rotation));
        rotM.getInverse(GWTThreeUtils.rotationToMatrix4(rotation));

        rotM.multiplyVector3(newPos);

        // Vector3 parentRotation=THREE.Vector3();

        /*
        Object3D obj=selectedDraggablekObject;
        while(obj.getParent()!=null){
        	parentPos.addSelf(obj.getParent().getPosition());
        	parentRotation.addSelf(obj.getParent().getRotation());
        	obj=obj.getParent();
        }

        LogUtils.log("parent-rot:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(parentRotation)));
        */
        // Matrix4 mx=GWTThreeUtils.rotationToMatrix4(parentRotation);
        // mx.multiplyVector3(parentPos);

        /*
        log("m:"+ThreeLog.get(selectedDraggablekObject.getPosition()));
        log("m:"+ThreeLog.get(GWTThreeUtils.toPositionVec(selectedDraggablekObject.getMatrix())));
        log("mw:"+ThreeLog.get(GWTThreeUtils.toPositionVec(selectedDraggablekObject.getMatrixWorld())));
        */
        // must be same as selectedDraggablekObject
        return newPos;
      } catch (Exception e) {
        LogUtils.log("moveSelectionPosition:" + e.getMessage());
        return null;
      }
    } else {
      return null;
    }
  }
public class GWTDragObjectControler {
  private Projector projector;
  private Mesh mouseCatchPlane;

  public Mesh getMouseCatchPlane() {
    return mouseCatchPlane;
  }

  public GWTDragObjectControler(Scene scene, Projector projector) {
    this.projector = projector;
    // maybe should black&transparent like js-sample
    mouseCatchPlane =
        THREE.Mesh(
            THREE.PlaneGeometry(2000, 2000, 10, 10),
            THREE.MeshBasicMaterial().color(0x00ffff).wireFrame().build());
    mouseCatchPlane.setVisible(false);
    scene.add(mouseCatchPlane);
  }

  private Object3D selectedDraggablekObject;
  private Object3D intersectedDraggablekObject;

  public void copyIntersectedPosition() {
    // intersectedDraggablekObject.updateMatrixWorld(true);
    mouseCatchPlane
        .getPosition()
        .copy(GWTThreeUtils.toPositionVec(intersectedDraggablekObject.getMatrixWorld()));
  }

  public Object3D getIntersectedDraggablekObject() {
    return intersectedDraggablekObject;
  }

  public void setIntersectedDraggablekObject(Object3D intersectedDraggablekObject) {
    this.intersectedDraggablekObject = intersectedDraggablekObject;
    copyIntersectedPosition();
  }

  public Object3D getSelectedDraggablekObject() {
    return selectedDraggablekObject;
  }

  private Vector3 draggableOffset = THREE.Vector3();

  public Vector3 getDraggableOffset() {
    return draggableOffset;
  }

  // target must be rayed
  /*
   *** sometime it error happend,check
   * double mouseMoved=(Math.abs(diffX)+Math.abs(diffY));
   *double length=newPos.clone().subSelf(getCurrentIkData().getTargetPos()).length();
   *  if(length<mouseMoved*5){
   */
  public Vector3 moveSelectionPosition(
      int mouseX, int mouseY, int screenWidth, int screenHeight, Camera camera) {
    if (isSelected()) {
      try {
        Ray ray = projector.gwtCreateRay(mouseX, mouseY, screenWidth, screenHeight, camera);
        JsArray<Intersect> intersects = ray.intersectObject(mouseCatchPlane);

        Vector3 newPos = intersects.get(0).getPoint().subSelf(draggableOffset);
        log = "newPos-raw:" + ThreeLog.get(newPos);
        Vector3 parentPos = THREE.Vector3();
        if (selectedDraggablekObject.getParent() != null) {
          // selectedDraggablekObject.getParent().updateMatrixWorld(true); //if call it bone moving
          // will be broken.i have no idea.
          parentPos =
              GWTThreeUtils.toPositionVec(selectedDraggablekObject.getParent().getMatrixWorld());
          // LogUtils.log("parent:"+ThreeLog.get(parentPos));
          log += "parentPos:" + ThreeLog.get(parentPos);
        }
        newPos.subSelf(parentPos);

        Matrix4 rotM = THREE.Matrix4();
        Vector3 rotation =
            GWTThreeUtils.rotationToVector3(
                (selectedDraggablekObject.getParent().getMatrixWorld()));
        // rotM.getInverse(selectedDraggablekObject.getMatrixRotationWorld());

        log += "parentRot:" + ThreeLog.get(GWTThreeUtils.radiantToDegree(rotation));
        rotM.getInverse(GWTThreeUtils.rotationToMatrix4(rotation));

        rotM.multiplyVector3(newPos);

        // Vector3 parentRotation=THREE.Vector3();

        /*
        Object3D obj=selectedDraggablekObject;
        while(obj.getParent()!=null){
        	parentPos.addSelf(obj.getParent().getPosition());
        	parentRotation.addSelf(obj.getParent().getRotation());
        	obj=obj.getParent();
        }

        LogUtils.log("parent-rot:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(parentRotation)));
        */
        // Matrix4 mx=GWTThreeUtils.rotationToMatrix4(parentRotation);
        // mx.multiplyVector3(parentPos);

        /*
        log("m:"+ThreeLog.get(selectedDraggablekObject.getPosition()));
        log("m:"+ThreeLog.get(GWTThreeUtils.toPositionVec(selectedDraggablekObject.getMatrix())));
        log("mw:"+ThreeLog.get(GWTThreeUtils.toPositionVec(selectedDraggablekObject.getMatrixWorld())));
        */
        // must be same as selectedDraggablekObject
        return newPos;
      } catch (Exception e) {
        LogUtils.log("moveSelectionPosition:" + e.getMessage());
        return null;
      }
    } else {
      return null;
    }
  }

  private String log;

  public String getLog() {
    return log;
  }

  public void selectObject(
      Object3D target, int mouseX, int mouseY, int screenWidth, int screenHeight, Camera camera) {
    try {
      selectedDraggablekObject = target;
      Ray ray = projector.gwtCreateRay(mouseX, mouseY, screenWidth, screenHeight, camera);

      // ig dry every thing dont work
      // Vector3
      // rotation=GWTThreeUtils.rotationToVector3((selectedDraggablekObject.getMatrixWorld()));
      // mouseClickCatcher.getRotation().copy(rotation);

      selectedDraggablekObject.updateMatrixWorld(true);
      mouseCatchPlane
          .getPosition()
          .copy(GWTThreeUtils.toPositionVec(selectedDraggablekObject.getMatrixWorld()));
      mouseCatchPlane.updateMatrixWorld(true); // very important

      JsArray<Intersect> pintersects = ray.intersectObject(mouseCatchPlane);
      draggableOffset.copy(pintersects.get(0).getPoint()).subSelf(mouseCatchPlane.getPosition());
      /*
       * make a problem?
      if(draggableOffset.getX()<0.0001){
      	draggableOffset.setX(0);
      }
      if(draggableOffset.getY()<0.0001){
      	draggableOffset.setY(0);
      }
      if(draggableOffset.getZ()<0.0001){
      	draggableOffset.setZ(0);
      }
      */

    } catch (Exception e) {
      LogUtils.log("selectObject:" + e.getMessage());
      selectedDraggablekObject = null;
    }
    // LogUtils.log("offset:"+ThreeLog.get(draggableOffset));
  }

  public void unselectObject() {
    if (selectedDraggablekObject != null) {
      selectedDraggablekObject = null;
    }
  }

  public boolean isSelected() {
    return selectedDraggablekObject != null;
  }
}