org.jbox2d.testbed.tests.LiquidTest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbox2d-testbed Show documentation
Show all versions of jbox2d-testbed Show documentation
The testbed for JBox2D, a 2d java physics engine, ported from the C++ Box2d engine.
/*******************************************************************************
* 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 boolean firstTime = true;
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[][] 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();
}
}
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 neighbors = new ArrayList();
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