Package org.jbox2d.testbed.tests

Source Code of org.jbox2d.testbed.tests.LiquidTest

/*******************************************************************************
* Copyright (c) 2013, Daniel Murphy All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met: * Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following disclaimer. * Redistributions
* in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package org.jbox2d.testbed.tests;

import java.util.ArrayList;
import java.util.Arrays;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.MassData;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.testbed.framework.TestbedSettings;
import org.jbox2d.testbed.framework.TestbedTest;

// TODO make this liquid usable for developers
/**
* The dynamic tree broadphase doesn't really suite this test well.
*/
public class LiquidTest extends TestbedTest {
  private int nParticles = 1000;
  private float totalMass = 10.0f;
  private float boxWidth = 2.0f;
  private float boxHeight = 20.0f;

  private float fluidMinX = -11.0f;
  private float fluidMaxX = 5.0f;
  private float fluidMinY = -10.0f;
  private float fluidMaxY = 10.0f;

  private Body[] liquid;

  private float rad = 0.6f;
  private float visc = 0.004f;// 0.005f;

  private ArrayList<Integer>[][] hash;
  private int hashWidth, hashHeight;

  private int hashX(float x) {
    float f = MathUtils.map(x, fluidMinX, fluidMaxX, 0, hashWidth - .001f);
    return (int) f;
  }

  private int hashY(float y) {
    float f = MathUtils.map(y, fluidMinY, fluidMaxY, 0, hashHeight - .001f);
    return (int) f;
  }

  @SuppressWarnings("unchecked")
  public LiquidTest() {
    hash = new ArrayList[40][40];
    for (int i = 0; i < 40; ++i) {
      for (int j = 0; j < 40; ++j) {
        hash[i][j] = new ArrayList<Integer>();
      }
    }
    hashWidth = 40;
    hashHeight = 40;
  }

  private void hashLocations() {
    for (int a = 0; a < hashWidth; a++) {
      for (int b = 0; b < hashHeight; b++) {
        hash[a][b].clear();
      }
    }

    for (int a = 0; a < liquid.length; a++) {
      int hcell = hashX(liquid[a].m_sweep.c.x);
      int vcell = hashY(liquid[a].m_sweep.c.y);
      if (hcell > -1 && hcell < hashWidth && vcell > -1 && vcell < hashHeight)
        hash[hcell][vcell].add(new Integer(a));
    }
  }

  private void applyLiquidConstraint(float deltaT) {
    /*
     * Unfortunately, this simulation method is not actually scale invariant, and it breaks down for
     * rad < ~3 or so. So we need to scale everything to an ideal rad and then scale it back after.
     */
    final float idealRad = 50.0f;
    float multiplier = idealRad / rad;

    float[] xchange = new float[liquid.length];
    float[] ychange = new float[liquid.length];
    Arrays.fill(xchange, 0.0f);
    Arrays.fill(ychange, 0.0f);

    float[] xs = new float[liquid.length];
    float[] ys = new float[liquid.length];
    float[] vxs = new float[liquid.length];
    float[] vys = new float[liquid.length];
    for (int i = 0; i < liquid.length; ++i) {
      xs[i] = multiplier * liquid[i].m_sweep.c.x;
      ys[i] = multiplier * liquid[i].m_sweep.c.y;
      vxs[i] = multiplier * liquid[i].m_linearVelocity.x;
      vys[i] = multiplier * liquid[i].m_linearVelocity.y;
    }

    for (int i = 0; i < liquid.length; i++) {
      // Populate the neighbor list from the 9 proximate cells
      ArrayList<Integer> neighbors = new ArrayList<Integer>();
      int hcell = hashX(liquid[i].m_sweep.c.x);
      int vcell = hashY(liquid[i].m_sweep.c.y);
      for (int nx = -1; nx < 2; nx++) {
        for (int ny = -1; ny < 2; ny++) {
          int xc = hcell + nx;
          int yc = vcell + ny;
          if (xc > -1 && xc < hashWidth && yc > -1 && yc < hashHeight && hash[xc][yc].size() > 0) {
            for (int a = 0; a < hash[xc][yc].size(); a++) {
              Integer ne = (Integer) hash[xc][yc].get(a);
              if (ne != null && ne.intValue() != i) neighbors.add(ne);
            }
          }
        }
      }

      // Particle pressure calculated by particle proximity
      // Pressures = 0 iff all particles within range are idealRad distance away
      float[] vlen = new float[neighbors.size()];
      float p = 0.0f;
      float pnear = 0.0f;
      for (int a = 0; a < neighbors.size(); a++) {
        Integer n = (Integer) neighbors.get(a);
        int j = n.intValue();
        float vx = xs[j] - xs[i];// liquid[j].m_sweep.c.x - liquid[i].m_sweep.c.x;
        float vy = ys[j] - ys[i];// liquid[j].m_sweep.c.y - liquid[i].m_sweep.c.y;

        // early exit check
        if (vx > -idealRad && vx < idealRad && vy > -idealRad && vy < idealRad) {
          float vlensqr = (vx * vx + vy * vy);
          // within idealRad check
          if (vlensqr < idealRad * idealRad) {
            vlen[a] = (float) Math.sqrt(vlensqr);
            if (vlen[a] < Settings.EPSILON) vlen[a] = idealRad - .01f;
            float oneminusq = 1.0f - (vlen[a] / idealRad);
            p = (p + oneminusq * oneminusq);
            pnear = (pnear + oneminusq * oneminusq * oneminusq);
          } else {
            vlen[a] = Float.MAX_VALUE;
          }
        }
      }

      // Now actually apply the forces
      // System.out.println(p);
      float pressure = (p - 5F) / 2.0F; // normal pressure term
      float presnear = pnear / 2.0F; // near particles term
      float changex = 0.0F;
      float changey = 0.0F;
      for (int a = 0; a < neighbors.size(); a++) {
        Integer n = (Integer) neighbors.get(a);
        int j = n.intValue();
        float vx = xs[j] - xs[i];// liquid[j].m_sweep.c.x - liquid[i].m_sweep.c.x;
        float vy = ys[j] - ys[i];// liquid[j].m_sweep.c.y - liquid[i].m_sweep.c.y;
        if (vx > -idealRad && vx < idealRad && vy > -idealRad && vy < idealRad) {
          if (vlen[a] < idealRad) {
            float q = vlen[a] / idealRad;
            float oneminusq = 1.0f - q;
            float factor = oneminusq * (pressure + presnear * oneminusq) / (2.0F * vlen[a]);
            float dx = vx * factor;
            float dy = vy * factor;
            float relvx = vxs[j] - vxs[i];
            float relvy = vys[j] - vys[i];
            factor = visc * oneminusq * deltaT;
            dx -= relvx * factor;
            dy -= relvy * factor;
            // liquid[j].m_xf.position.x += dx;//*deltaT*deltaT;
            // liquid[j].m_xf.position.y += dy;//*deltaT*deltaT;
            // liquid[j].m_linearVelocity.x += dx;///deltaT;//deltaT;
            // liquid[j].m_linearVelocity.y += dy;///deltaT;//deltaT;
            xchange[j] += dx;
            ychange[j] += dy;
            changex -= dx;
            changey -= dy;
          }
        }
      }
      // liquid[i].m_xf.position.x += changex;//*deltaT*deltaT;
      // liquid[i].m_xf.position.y += changey;//*deltaT*deltaT;
      // liquid[i].m_linearVelocity.x += changex;///deltaT;//deltaT;
      // liquid[i].m_linearVelocity.y += changey;///deltaT;//deltaT;
      xchange[i] += changex;
      ychange[i] += changey;
    }
    // multiplier *= deltaT;
    for (int i = 0; i < liquid.length; ++i) {
      liquid[i].m_xf.p.x += xchange[i] / multiplier;
      liquid[i].m_xf.p.y += ychange[i] / multiplier;
      liquid[i].m_linearVelocity.x += xchange[i] / (multiplier * deltaT);
      liquid[i].m_linearVelocity.y += ychange[i] / (multiplier * deltaT);
    }

  }

  @Override
  public Vec2 getDefaultCameraPos() {
    return new Vec2(0, 2);
  }
 
  @Override
  public float getDefaultCameraScale() {
    return 35f;
  }

  @Override
  public void initTest(boolean argDeserialized) {

    Body ground = null;
    {

      BodyDef bd = new BodyDef();
      bd.position.set(0.0f, 0.0f);
      ground = getWorld().createBody(bd);
      PolygonShape shape = new PolygonShape();
      shape.setAsBox(5.0f, 0.5f);
      ground.createFixture(shape, 0);

      shape.setAsBox(1.0f, 0.2f, new Vec2(0.0f, 4.0f), -0.2f);
      ground.createFixture(shape, 0);
      shape.setAsBox(1.5f, 0.2f, new Vec2(-1.2f, 5.2f), -1.5f);
      ground.createFixture(shape, 0);
      shape.setAsBox(0.5f, 50.0f, new Vec2(5.0f, 0.0f), 0.0f);
      ground.createFixture(shape, 0);



      shape.setAsBox(0.5f, 3.0f, new Vec2(-8.0f, 0.0f), 0.0f);
      ground.createFixture(shape, 0);

      shape.setAsBox(2.0f, 0.1f, new Vec2(-6.0f, -2.8f), 0.1f);
      ground.createFixture(shape, 0);

      CircleShape cd = new CircleShape();
      cd.m_radius = 0.5f;
      cd.m_p.set(-0.5f, -4.0f);
      ground.createFixture(cd, 0);

    }

    liquid = new Body[nParticles];
    float massPerParticle = totalMass / nParticles;
    // PointDef pd = new PointDef();
    // pd.mass = massPerParticle;
    // pd.restitution = 0.0f;
    // pd.filter.groupIndex = -10;
    CircleShape pd = new CircleShape();
    FixtureDef fd = new FixtureDef();
    fd.shape = pd;
    fd.density = 1f;
    fd.filter.groupIndex = -10;
    pd.m_radius = .05f;
    fd.restitution = 0.4f;
    fd.friction = 0.0f;
    float cx = 0.0f;
    float cy = 25.0f;
    for (int i = 0; i < nParticles; ++i) {
      BodyDef bd = new BodyDef();
      bd.position =
          new Vec2(MathUtils.randomFloat(cx - boxWidth * .5f, cx + boxWidth * .5f),
              MathUtils.randomFloat(cy - boxHeight * .5f, cy + boxHeight * .5f));
      bd.fixedRotation = true;
      bd.type = BodyType.DYNAMIC;
      Body b = getWorld().createBody(bd);

      b.createFixture(fd).setUserData(LIQUID_INT);

      MassData md = new MassData();
      md.mass = massPerParticle;
      md.I = 1.0f;
      b.setMassData(md);
      b.setSleepingAllowed(false);
      liquid[i] = b;
    }

    PolygonShape polyDef = new PolygonShape();
    polyDef.setAsBox(MathUtils.randomFloat(0.3f, 0.7f), MathUtils.randomFloat(0.3f, 0.7f));
    BodyDef bodyDef = new BodyDef();
    bodyDef.position = new Vec2(0.0f, 25.0f);
    bodyDef.type = BodyType.DYNAMIC;
    bod = getWorld().createBody(bodyDef);
    bod.createFixture(polyDef, 1f);
  }

  // IF YOU CHANGE THIS change the corresponding value in World
  public static Integer LIQUID_INT = new Integer(1234598372);

  private Body bod;

  @Override
  public void step(TestbedSettings settings) {
    super.step(settings);
    float hz = settings.getSetting(TestbedSettings.Hz).value;
    float dt = 1.0f / hz;

    int n = 1;
    for (int i = 0; i < n; ++i) {
      hashLocations();
      applyLiquidConstraint(dt * n);
    }
    dampenLiquid();

    checkBounds();

    // System.out.println(m_getWorld().getGroundBody().getXForm());
  }

  private void checkBounds() {
    for (int i = 0; i < liquid.length; ++i) {
      if (liquid[i].getWorldCenter().y < -10.0f) {
        getWorld().destroyBody(liquid[i]);
        float massPerParticle = totalMass / nParticles;

        CircleShape pd = new CircleShape();
        FixtureDef fd = new FixtureDef();
        fd.shape = pd;
        fd.density = 1.0f;
        fd.filter.groupIndex = -10;
        pd.m_radius = .05f;
        fd.restitution = 0.4f;
        fd.friction = 0.0f;
        float cx = 0.0f + MathUtils.randomFloat(-0.6f, 0.6f);
        float cy = 15.0f + MathUtils.randomFloat(-2.3f, 2.0f);
        BodyDef bd = new BodyDef();
        bd.position = new Vec2(cx, cy);
        bd.fixedRotation = true;
        bd.type = BodyType.DYNAMIC;
        Body b = getWorld().createBody(bd);
        b.createFixture(fd).setUserData(LIQUID_INT);
        MassData md = new MassData();
        md.mass = massPerParticle;
        md.I = 1.0f;
        b.setMassData(md);
        b.setSleepingAllowed(false);
        liquid[i] = b;
      }
    }

    if (bod.getWorldCenter().y < -15.0f) {
      getWorld().destroyBody(bod);
      PolygonShape polyDef = new PolygonShape();
      polyDef.setAsBox(MathUtils.randomFloat(0.3f, 0.7f), MathUtils.randomFloat(0.3f, 0.7f));
      BodyDef bodyDef = new BodyDef();
      bodyDef.position = new Vec2(0.0f, 25.0f);
      bodyDef.type = BodyType.DYNAMIC;
      bod = getWorld().createBody(bodyDef);
      bod.createFixture(polyDef, 1f);
    }
  }

  private void dampenLiquid() {
    for (int i = 0; i < liquid.length; ++i) {
      Body b = liquid[i];
      b.setLinearVelocity(b.getLinearVelocity().mul(0.995f));
    }
  }


  @Override
  public String getTestName() {
    return "Liquid Test";
  }
}
TOP

Related Classes of org.jbox2d.testbed.tests.LiquidTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.