Main Menu

Cloth Cape

Started by AGP, September 13, 2013, 10:21:00 AM

Previous topic - Next topic

AGP

I've been playing around Mizuki's and your cloth simulator. I've been trying to make a cape. I've replaced the sphere with my character as the colliding object. The problem is that the cloth seems to fold onto itself. It doesn't fall to the ground, either, it hangs there. I've included both the screenshot and mine and your source code.




//THIS CLASS SORT OF REPLACES ClothTester. I initialize it with cape = new ClothCape(manager, theWorld, batman.model.get(0));
     public ClothCape(TextureManager manager, World theWorld, raft.jpct.bones.Animated3D batman) {
this.theWorld = theWorld;
this.batman = batman;
manager.addTexture("twinkle", new Texture("blue.png"));
manager.addTexture("table", new Texture("brown.png"));

cape = Primitives.getPlane(10, 1);
cape.translate(0, -15f, 5f);
cape.setTexture("twinkle");
cape.setCulling(Object3D.CULLING_DISABLED);
cape.build();
theWorld.addObject(cape);
batman.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);

clothSim = new ClothController(theWorld, true, 11, 11, 50f);
cape.getMesh().setVertexController(clothSim, IVertexController.PRESERVE_SOURCE_MESH);
     }
     public void doMovement() {//FROM YOUR ClothTester
           ....
     }
}



import com.threed.jpct.*;
import java.lang.Math;

/**
* Based on the orginal source by Mizuki Takase. Optimized for Android by EgonOlsen.
*
* @author Mizuki Takase, EgonOlsen
*
*/
public class ClothController extends GenericVertexController {

private static final long serialVersionUID = 1L;

private int gridx;
private int gridy;
private float spacing;
private float stff;
private float mass;
private float dt;
private SimpleVector gravity;
private int numParticles;
private int numConstraints;
private int[] index;
private Particle[] particles;
private SimpleVector[] forces;
private SpringConstraint[] constraints;
private World theWorld;
private boolean freezeTop;

/*
* when inputting in the grid x and y, don't forget that this is for the
* amount of lines that make up the grid.
*/
public ClothController(World w, int x, int y, float sp) {
theWorld = w;
freezeTop = false;
gridx = x;
gridy = y;
spacing = sp;
stff = 5f;
mass = 0.5f; // 0.5
dt = 0.05f; // 0.05
gravity = new SimpleVector(0F, 9F, 0F);
}

public ClothController(World w, boolean f, int x, int y, float sp) {
theWorld = w;
freezeTop = f;
gridx = x;
gridy = y;
spacing = sp;
stff = 5f;
mass = 0.5f;
dt = 0.05f;
gravity = new SimpleVector(0F, 9F, 0F);
}

/*
* when inputting in the grid x and y, don't forget that this is for the
* amount of lines that make up the grid.
*/
public ClothController(World w, boolean f, int x, int y, float sp, float st, float m, float t, SimpleVector g) {
theWorld = w;
freezeTop = f;
gridx = x;
gridy = y;
spacing = sp;
stff = st;
mass = m;
dt = t;
gravity = g;
}

public boolean setup() {
index = QSort.sort(getSourceMesh());

numConstraints = (gridx - 1) * gridy + (gridy - 1) * gridx + (gridx - 1) * 2 * (gridy - 1);
numParticles = gridx * gridy;
constraints = new SpringConstraint[numConstraints];
particles = new Particle[numParticles];
forces = new SimpleVector[numParticles];

for (int i = 0; i < numParticles; i++) {
if (i > numParticles - gridx)
mass = 5;
particles[i] = new Particle(getSourceMesh()[i].x, getSourceMesh()[i].y, getSourceMesh()[i].z, mass);
particles[i].velocity = new SimpleVector(0F, 0F, 0F);
}

for (int i = 0; i < forces.length; i++) {
forces[i] = new SimpleVector();
}

float diagstff = (float) Math.sqrt(stff * stff + stff * stff);
float rt2 = (float) Math.sqrt(spacing * spacing + spacing * spacing);
int off = 0;
for (int i = 0; i < numParticles; i++) {
if (i % (gridx) < gridx - 1 && i < numParticles - gridx || i == 0) {
constraints[off++] = new SpringConstraint(particles[i], particles[i + 1], spacing, stff);
constraints[off++] = new SpringConstraint(particles[i], particles[i + gridx], spacing, stff);
constraints[off++] = new SpringConstraint(particles[i], particles[i + gridx + 1], rt2, diagstff);
if (i % gridx > 0) {
constraints[off++] = new SpringConstraint(particles[i], particles[i + gridx - 1], rt2, diagstff);
}
} else if ((i % (gridx) == gridx - 1) && (i < (gridx * gridy - (gridx)))) {
constraints[off++] = new SpringConstraint(particles[i], particles[i + gridx], spacing, stff);
constraints[off++] = new SpringConstraint(particles[i], particles[i + gridx - 1], rt2, diagstff);
} else if (i > numParticles - 2) {
} else {
constraints[off++] = new SpringConstraint(particles[i], particles[i + 1], spacing, stff);
}
}
return true;
}

public void apply() {
resolveConstraints();

int j;
if (freezeTop) {
j = gridx;
} else {
j = 0;
}
int end = particles.length;
for (; j < end; j++) {

Particle particlesj = particles[j];
SimpleVector forcesj = forces[j];

particlesj.oldPosition.set(particlesj.currentPosition);

particles[j].acceleration.set((float) (particlesj.acceleration.x + (forcesj.x / particlesj.mass) - 0.05 * particlesj.velocity.x), (float) (particlesj.acceleration.y
+ (forcesj.y / particlesj.mass) - 0.05 * particlesj.velocity.y),
(float) (particlesj.acceleration.z + (forcesj.z / particlesj.mass) - 0.05 * particlesj.velocity.z));

float vx = particlesj.velocity.x;
float vy = particlesj.velocity.y;
float vz = particlesj.velocity.z;
float p = 1F;

particlesj.velocity.set(particlesj.velocity.x + particlesj.acceleration.x * dt, particlesj.velocity.y + particlesj.acceleration.y * dt, particlesj.velocity.z
+ particlesj.acceleration.z * dt);

particlesj.currentPosition.set(particlesj.currentPosition.x + ((1F - p) * vx + p * particlesj.velocity.x) * dt, particlesj.currentPosition.y
+ ((1F - p) * vy + p * particlesj.velocity.y) * dt, particlesj.currentPosition.z + ((1F - p) * vz + p * particlesj.velocity.z) * dt);

particlesj.acceleration.set(0F, 0F, 0F);

if (theWorld.checkCollision(particlesj.currentPosition, particlesj.velocity, 20) != Object3D.NO_OBJECT) {
particlesj.currentPosition.set(particlesj.oldPosition);
particlesj.velocity.set(0, 0, 0);
}

// apply force
forcesj.set(gravity);
}

for (int i = 0; i < particles.length; i++) {
getDestinationMesh()[index[i]].set(particles[i].currentPosition);
}
}

void resolveConstraints() {
for (int j = 0; j < constraints.length; j++) {
constraints[j].resolveConstraint();
}
}

public Particle getParticle(SimpleVector v) {
if (v != null) {
float mindist = 1000.0F;
int index = 0;
for (int i = 0; i < particles.length; i++) {
SimpleVector pic = particles[i].currentPosition;
float deltax = pic.x - v.x;
float deltay = pic.y - v.y;
float distance = (float) Math.sqrt(deltax * deltax + deltay * deltay);

if (distance < mindist) {
mindist = distance;
index = i;
}
}
return particles[index];
} else
return null;
}

}

class SpringConstraint {

private Particle a, b;
private float restLen;
private float stiffness;

public SpringConstraint(Particle pa, Particle pb, float restlength, float stiff) {
a = pa;
b = pb;
restLen = restlength;
stiffness = stiff;
}

void resolveConstraint() {
if (a == b)
return;
else {
SimpleVector acurrentPosition = a.currentPosition;
SimpleVector bcurrentPosition = b.currentPosition;

SimpleVector aacceleration = a.acceleration;
SimpleVector bacceleration = b.acceleration;

SimpleVector avelocity = a.velocity;
SimpleVector bvelocity = b.velocity;

float amass1 = 1f / a.mass;
float bmass1 = 1f / b.mass;

float deltax = acurrentPosition.x - bcurrentPosition.x;
float deltay = acurrentPosition.y - bcurrentPosition.y;
float deltaz = acurrentPosition.z - bcurrentPosition.z;
float distance = (float) Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);

float diff = (distance - restLen) * (stiffness / distance);

float dvx = avelocity.x - bvelocity.x;
float dvy = avelocity.y - bvelocity.y;
float dvz = avelocity.z - bvelocity.z;

float dmp = dvx * deltax + dvy * deltay + dvz * deltaz;
dmp /= distance * distance;
dmp *= 2.0;

a.acceleration.set((float) (aacceleration.x - deltax * (dmp + diff) * (amass1)), (float) (aacceleration.y - deltay * (dmp + diff) * (amass1)),
(float) (aacceleration.z - deltaz * (dmp + diff) * (amass1)));

b.acceleration.set((float) (bacceleration.x + deltax * (dmp + diff) * (bmass1)), (float) (bacceleration.y + deltay * (dmp + diff) * (bmass1)),
(float) (bacceleration.z + deltaz * (dmp + diff) * (bmass1)));
}
}
}

class Particle {
SimpleVector currentPosition;
SimpleVector oldPosition;
SimpleVector acceleration;
SimpleVector velocity;
float mass;

public Particle(float x, float y, float z, float m) {
this.currentPosition = new SimpleVector(x, y, z);
this.oldPosition = new SimpleVector(x, y, z);
this.acceleration = new SimpleVector(0.0001F, 0.0001F, 0.0001F);
this.velocity = new SimpleVector(0.0001F, 0.0001F, 0.0001F);
this.mass = m;
}

void setPosition(float xnew, float ynew, float znew) {
this.oldPosition.set(this.currentPosition);
this.currentPosition.set(xnew, ynew, znew);
}
}

class QSort {
static int[] QuickSort(SimpleVector[] a, int[] b, int lo0, int hi0) {
int lo = lo0;
int hi = hi0;
SimpleVector mid;

if (hi0 > lo0) {
mid = a[(lo0 + hi0) / 2];
while (lo <= hi) {
while ((lo < hi0) && (a[lo].y < mid.y || (a[lo].x < mid.x && a[lo].y == mid.y))) {
++lo;
}

while ((hi > lo0) && (a[hi].y > mid.y || (a[hi].y == mid.y && a[hi].x > mid.x))) {
--hi;
}
if (lo <= hi) {
swap(a, lo, hi);
swap(b, lo, hi);

++lo;
--hi;
}
}
if (lo0 < hi) {
QuickSort(a, b, lo0, hi);
}
if (lo < hi0) {
QuickSort(a, b, lo, hi0);
}
}

return b;
}

private static final void swap(int a[], int i, int j) {
int T = a[i];
a[i] = a[j];
a[j] = T;
}

private static final void swap(SimpleVector a[], int i, int j) {
SimpleVector T = a[i];
a[i] = a[j];
a[j] = T;
}

public static final int[] sort(SimpleVector a[]) {
int[] temp = new int[a.length];
for (int i = 0; i < temp.length; i++) {
temp[i] = i;
}
QuickSort(a, temp, 0, a.length - 1);
return temp;
}
}



/**
ORIGINALLY DATED Oct. 17, 2011. IS THIS Helge's OR Mizuki's?
*/

import java.io.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

import com.threed.jpct.*;
import com.threed.jpct.util.*;

class ClothTester  {
private final static float MOVE_SPEED = 2.5f;
private final static float TURN_SPEED = 0.06f;
private Matrix playerDirection = new Matrix();
private SimpleVector tempVector = new SimpleVector();

private final static int SWITCH_RENDERER = 35;
private boolean fullscreen = false;
private boolean openGL = false;
private boolean wireframe = false;

// some jPCT related stuff...
private Object3D obj;
private Object3D sphere;
private ClothController clothSim;
private FrameBuffer buffer = null;
private World theWorld = null;
private TextureManager texMan = null;
private Camera camera = null;
private Texture numbers = null;
private int width = 640;
private int height = 480;

// some AWT related stuff
private Frame frame = null;
private Graphics gFrame = null;
private BufferStrategy bufferStrategy = null;
private GraphicsDevice device = null;
private KeyMapper keyMapper = null;
private int titleBarHeight = 0;
private int leftBorderWidth = 0;
private int switchMode = 0;
private int fps;
private int lastFps;
private long totalFps;
private int pps;
private int lastPps;
private boolean isIdle = false;
private boolean exit = false;

// direction
private boolean left = false;
private boolean right = false;
private boolean forward = false;
private boolean back = false;
private boolean up = false;
private boolean down = false;
private int mouseX;
private int mouseY;

// ball movement
private boolean ballfoward = false;
private boolean ballback = false;
private boolean ballleft = false;
private boolean ballright = false;
private boolean ballup = false;
private boolean balldown = false;

// mouse picker
private Particle selected = null;

public static void main(String[] args) {
ClothTester start = new ClothTester(args);
}

/**
* The constructor. Here we are initializing things...
*/
private ClothTester(String[] args) {

Config.useMultipleThreads = true;
Config.mtDebug = true;
Config.loadBalancingStrategy = 1;
Config.maxNumberOfCores = Runtime.getRuntime().availableProcessors();

isIdle = false;
switchMode = 0;
totalFps = 0;
fps = 0;
lastFps = 0;

/**
* Initialize the World instance and get the TextureManager (a
* singleton)
*/
theWorld = new World();
texMan = TextureManager.getInstance();

/**
* Setup the lighting. We are not using overbright lighting because the
* OpenGL renderer can't do it, but we are using RGB-scaling. Some
* hardware/drivers for OpenGL don't support this.
*/
Config.fadeoutLight = false;
Config.lightDiscardDistance = 3500;
Config.farPlane = 5000;
theWorld.getLights().setOverbrightLighting(Lights.OVERBRIGHT_LIGHTING_DISABLED);
theWorld.setAmbientLight(100, 150, 150);

/**
* Place the lightsources...
*/
theWorld.addLight(new SimpleVector(0, 0, 300), 100, 100, 100);

/**
* Load the textures needed and add them to the TextureManager. We are
* loading the "numbers" texture for blitting the framerate as well as
* the weapon's environment map and all JPGs that can be found in the
* "textures"-directory. The 3DS file of the level contains materials
* that are pointing to these textures (identified by name).
*/
numbers = new Texture("numbers.jpg");
texMan.addTexture("numbers", numbers);
texMan.addTexture("twinkle", new Texture("blue.png"));
texMan.addTexture("table", new Texture("brown.png"));

obj = Primitives.getPlane(10, 50);
obj.rotateX((float) Math.PI / 2);
obj.rotateMesh();
obj.setRotationMatrix(new Matrix());
obj.setTexture("twinkle");
obj.setCulling(Object3D.CULLING_DISABLED);
obj.build();
theWorld.addObject(obj);

clothSim = new ClothController(theWorld, true, 11, 11, 50f);
obj.getMesh().setVertexController(clothSim, IVertexController.PRESERVE_SOURCE_MESH);

sphere = Primitives.getSphere(150F);
// sphere = Primitives.getCone(150F);
// sphere = Primitives.getCube(150F);
sphere.translate(0f, 375f, 0f);
sphere.setTexture("table");
sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
sphere.build();
theWorld.addObject(sphere);

camera = theWorld.getCamera();
camera.setPosition(new SimpleVector(0, -50, -400));
camera.lookAt(sphere.getTransformedCenter());
camera.rotateX(0.3f);
theWorld.buildAllObjects();

sphere.compile();
obj.compile(true);

/**
* Setup some optimizations for outdoor rendering (the level is not very
* outdoorish at all, but at least the framebuffer needs clearing
* because the level is not closed.
*/
Config.collideOffset = 250;
Config.tuneForOutdoor();

/**
* Do some AWT setup work
*/
initializeFrame();

/**
* Here we go...!
*/
gameLoop();
}

/**
* This initializes the AWT frame either in fullscreen or in windowed mode.
* This is not a waterproof intialization, but i didn't want to do a AWT
* tutorial here (and i would be the wrong person to do this anyway...:-)).
* Change whatever you think that needs change here...
*/
private void initializeFrame() {
if (fullscreen) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
device = env.getDefaultScreenDevice();
GraphicsConfiguration gc = device.getDefaultConfiguration();
frame = new Frame(gc);
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
device.setFullScreenWindow(frame);
if (device.isDisplayChangeSupported()) {
device.setDisplayMode(new DisplayMode(width, height, 32, 0));
}
frame.createBufferStrategy(2);
bufferStrategy = frame.getBufferStrategy();
Graphics g = bufferStrategy.getDrawGraphics();
bufferStrategy.show();
g.dispose();
} else {
frame = new Frame();
frame.setTitle("jPCT " + Config.getVersion());
frame.pack();
Insets insets = frame.getInsets();
titleBarHeight = insets.top;
leftBorderWidth = insets.left;
frame.setSize(width + leftBorderWidth + insets.right, height + titleBarHeight + insets.bottom);
frame.setResizable(false);
frame.setVisible(true);
gFrame = frame.getGraphics();
}

/**
* The listeners are bound to the AWT frame...they are useless in OpenGL
* mode.
*/
frame.addWindowListener(new WindowEvents());
keyMapper = new KeyMapper(frame);

frame.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
}

public void mouseEntered(MouseEvent e) {
}

public void mouseExited(MouseEvent e) {
}

public void mousePressed(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
selected = clothSim.getParticle(getAbsoluteCoordinate(camera, buffer, mouseX, mouseY, obj));
}

public void mouseReleased(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
selected = null;
}
});

frame.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
if (selected != null) {
selected.velocity.x = (e.getX() - mouseX);
selected.velocity.y = (e.getY() - mouseY);
}
}

public void mouseMoved(MouseEvent e) {
}
});
}

/**
* get 3d coordinate of srcPoint on object's surface from 2d coordinates.
*
* @param camera
*            scene camera
* @param buffer
* @param x
*            screen (mouse) X coordinate
* @param y
*            screen (mouse) Y coordinate
* @param object
*            to check against
* @return
*/
public static SimpleVector getAbsoluteCoordinate(Camera camera, FrameBuffer buffer, int x, int y, Object3D object) {
SimpleVector rezult = null;
if (camera != null && buffer != null) {
SimpleVector rayTemp = Interact2D.reproject2D3D(camera, buffer, x, y);

rayTemp.normalize();
Matrix orient = camera.getFront();
float[] dump = orient.getDump();
SimpleVector ray = new SimpleVector();
ray.x = dump[0] * rayTemp.x + dump[1] * rayTemp.y + dump[2] * rayTemp.z + dump[3] * 1;
ray.y = dump[4] * rayTemp.x + dump[5] * rayTemp.y + dump[6] * rayTemp.z + dump[7] * 1;
ray.z = dump[8] * rayTemp.x + dump[9] * rayTemp.y + dump[10] * rayTemp.z + dump[11] * 1;

float distance = object.rayIntersectsAABB(camera.getPosition(), ray);

if (distance != Object3D.RAY_MISSES_BOX) {
rezult = new SimpleVector(camera.getPosition());
ray.scalarMul(distance);
rezult.add(ray);
}
}
return rezult;
}

/**
* The fps counter is blitted into the rendered framebuffer here and the
* results will be displayed (hence the name of the method...:-))
*/
private void display() {
blitNumber((int) totalFps, 5, 2);
blitNumber((int) lastPps, 5, 12);

if (!openGL) {
if (!fullscreen) {
buffer.display(gFrame, leftBorderWidth, titleBarHeight);
} else {
Graphics g = bufferStrategy.getDrawGraphics();
g.drawImage(buffer.getOutputBuffer(), 0, 0, null);
bufferStrategy.show();
g.dispose();
}
} else {
buffer.displayGLOnly();
}
}

/**
* A simple method that does the number-blitting.
*/
private void blitNumber(int number, int x, int y) {
if (numbers != null) {
String sNum = Integer.toString(number);
for (int i = 0; i < sNum.length(); i++) {
char cNum = sNum.charAt(i);
int iNum = cNum - 48;
buffer.blit(numbers, iNum * 5, 0, x, y, 5, 9, FrameBuffer.TRANSPARENT_BLITTING);
x += 5;
}
}
}

/**
* Does the collision detection and the movement of the player.
*/
private void doMovement() {

// forward and back may change during excution (threaded!), so we have
// to ensure to
// reset the camera only if has been changed before.
boolean cameraChanged = false;

if (forward) {
cameraChanged = true;
tempVector = playerDirection.getZAxis();
theWorld.checkCameraCollisionEllipsoid(tempVector, new SimpleVector(0, 0, 0), MOVE_SPEED, 5);
}
if (back) {
if (!cameraChanged) {
cameraChanged = true;
}
tempVector = playerDirection.getZAxis();
tempVector.scalarMul(-1f);
theWorld.checkCameraCollisionEllipsoid(tempVector, new SimpleVector(0, 0, 0), MOVE_SPEED, 5);
}

if (left) {
camera.rotateAxis(camera.getBack().getYAxis(), -TURN_SPEED);
playerDirection.rotateY(-TURN_SPEED);
}
if (right) {
camera.rotateAxis(camera.getBack().getYAxis(), TURN_SPEED);
playerDirection.rotateY(TURN_SPEED);
}
if (up) {
camera.rotateX(TURN_SPEED);
}
if (down) {
camera.rotateX(-TURN_SPEED);
}

if (ballfoward) {
SimpleVector a = obj.getZAxis();
a.scalarMul(3);
sphere.translate(a);
}
if (ballback) {
SimpleVector a = obj.getZAxis();
a.scalarMul(-3);
sphere.translate(a);
}
if (ballleft) {
SimpleVector a = obj.getXAxis();
a.scalarMul(-3);
sphere.translate(a);
}
if (ballright) {
SimpleVector a = obj.getXAxis();
a.scalarMul(3);
sphere.translate(a);
}
if (ballup) {
SimpleVector a = obj.getYAxis();
a.scalarMul(3);
sphere.translate(a);
}
if (balldown) {
SimpleVector a = obj.getYAxis();
a.scalarMul(-3);
sphere.translate(a);
}
}

/**
* Poll the keyboard using the KeyMapper from com.threed.jpct.util
*/
private void poll() {
KeyState state = null;
do {
state = keyMapper.poll();
if (state != KeyState.NONE) {
keyAffected(state);
}
} while (state != KeyState.NONE);
}

/**
* This is the game's main loop. We are doing some additional setup work
* here and rendering the scene in a loop, as well as calling doMovement()
* to handle the movement and the collision detection before each frame.
*/
private void gameLoop() {
World.setDefaultThread(Thread.currentThread());

buffer = new FrameBuffer(width, height, FrameBuffer.SAMPLINGMODE_NORMAL);
buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE);

Timer timer = new Timer(10);
timer.start();

Timer fpsTimer = new Timer(1000);
fpsTimer.start();

long timerTicks = 0;

while (!exit) {
if (!isIdle) {

long ticks = timer.getElapsedTicks();
timerTicks += ticks;

for (int i = 0; i < ticks; i++) {
doMovement();
obj.getMesh().applyVertexController();
obj.touch();
}

poll();

if (switchMode != 0) {
switchOptions();
}

sphere.setScale(0.9f);

buffer.clear();
theWorld.renderScene(buffer);

if (!wireframe) {
theWorld.draw(buffer);
} else {
theWorld.drawWireframe(buffer, Color.white);
}

sphere.setScale(1f);

buffer.update();
display();

fps++;
pps += theWorld.getVisibilityList().getSize();

if (fpsTimer.getElapsedTicks() > 0) {
totalFps = (fps - lastFps);
lastFps = fps;
lastPps = pps;
pps = 0;
}

Thread.yield();

} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}

if (openGL) {
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
} else {
if (fullscreen) {
device.setFullScreenWindow(null);
}
}
System.exit(0);
}

/**
* This is for switching settings. Currently, only switching from OpenGL to
* software and back is supported here.
*/
private void switchOptions() {
switch (switchMode) {
case (SWITCH_RENDERER): {
isIdle = true;
if (buffer.usesRenderer(IRenderer.RENDERER_OPENGL)) {
keyMapper.destroy();
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE, IRenderer.MODE_OPENGL);
openGL = false;
if (fullscreen) {
device.setFullScreenWindow(null);
}
frame.setVisible(false);
frame.dispose();
initializeFrame();
} else {
frame.setVisible(false);
keyMapper.destroy();
buffer.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
openGL = true;
keyMapper = new KeyMapper();
}
isIdle = false;
break;
}
}
switchMode = 0;
}

private void keyAffected(KeyState state) {
int code = state.getKeyCode();
boolean event = state.getState();
switch (code) {
case (KeyEvent.VK_ESCAPE): {
exit = event;
break;
}
case (KeyEvent.VK_LEFT): {
left = event;
break;
}
case (KeyEvent.VK_RIGHT): {
right = event;
break;
}
case (KeyEvent.VK_PAGE_UP): {
up = event;
break;
}
case (KeyEvent.VK_PAGE_DOWN): {
down = event;
break;
}
case (KeyEvent.VK_UP): {
forward = event;
break;
}
case (KeyEvent.VK_DOWN): {
back = event;
break;
}
case (KeyEvent.VK_1): {
if (event && buffer.supports(FrameBuffer.SUPPORT_FOR_RGB_SCALING)) {
theWorld.getLights().setRGBScale(Lights.RGB_SCALE_DEFAULT);
}
break;
}
case (KeyEvent.VK_2): { // 2x scaling
if (event && buffer.supports(FrameBuffer.SUPPORT_FOR_RGB_SCALING)) {
theWorld.getLights().setRGBScale(Lights.RGB_SCALE_2X);
}
break;
}
case (KeyEvent.VK_4): { // 4x scaling
if (event && buffer.supports(FrameBuffer.SUPPORT_FOR_RGB_SCALING)) {
theWorld.getLights().setRGBScale(Lights.RGB_SCALE_4X);
}
break;
}
case (KeyEvent.VK_W): { // wireframe mode (w)
if (event) {
wireframe = !wireframe;
}
break;
}
case (KeyEvent.VK_X): { // change renderer (x)
if (event) {
switchMode = SWITCH_RENDERER;
}
break;
}

case (KeyEvent.VK_I): {
ballfoward = event;
break;
}
case (KeyEvent.VK_K): {
ballback = event;
break;
}
case (KeyEvent.VK_J): {
ballleft = event;
break;
}
case (KeyEvent.VK_L): {
ballright = event;
break;
}
case (KeyEvent.VK_U): {
ballup = event;
break;
}
case (KeyEvent.VK_O): {
balldown = event;
break;
}
}
}

/**
* The WindowApapter used for software mode
*/
private class WindowEvents extends WindowAdapter {
public void windowClosing(WindowEvent e) {
exit = true;
}

public void windowIconified(WindowEvent e) {
isIdle = true;
}

public void windowDeiconified(WindowEvent e) {
isIdle = false;
}
}

private class Timer {
private long ticks = 0;
private long granularity = 0;

public Timer(int granularity) {
this.granularity = granularity;
}

public void start() {
ticks = System.currentTimeMillis();
}

public void reset() {
start();
}

public long getElapsedTicks() {
long cur = System.currentTimeMillis();
long l = cur - ticks;

if (l >= granularity) {
ticks = cur - (l % granularity);
return l / granularity;
}
return 0;
}
}
}

EgonOlsen

To be honest, i don't really know how this code works. All i did was to optimize it for Android. I never questioned it's inner workings much. IIRC, the cloth is attached to some fixed points (i can't remember where these come from, but if you look closely, you should be able to find that part). Maybe that's missing here, which might be reason why if just drops down? From my own tests, i recall that making it interact with complex meshes doesn't work really well. Maybe it's better to use a simplified collision mesh for the cloth instead of using the real model for the simulation.

AGP

I see your point. Certainly it would make the simulation run faster, but it would be harder both to program and to have a simplified model animated exactly like the main one. I'm not concerned about performance yet, and I can't waste days making a simpler Batman just for collision, so I'll keep trying with this one for now. If I could only see where the points from which it hangs are... My second approach was to use the actual model's cape as the cloth, but as that's a rigged model, raft's library produces a null reference for cape.getMesh(). This is my currently best approach (nowhere near good enough yet):