weird animation

Started by raft, March 20, 2013, 05:39:57 AM

Previous topic - Next topic

raft

found it. this happens if index is zero when animation is first applied.

you can use this as a test case with the serialized ball. if firstTime is set to false, there is no issue

long animIndex = 0;
long lastTime = System.currentTimeMillis();

boolean firstTime = true;

public void onDrawFrame(GL10 gl) {

long now = System.currentTimeMillis();
long millis = now - lastTime;
lastTime = now;

if (millis > 1000)
millis = 0;

animIndex += millis;
if (animIndex > 2000) {
animIndex = 0;
}
float anim = animIndex/2000f;
ball.animate(firstTime ? 0 : anim, 1);
firstTime = false;

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
fb.display();
}

raft

complete test case. how annoying, it was always here in front of my eyes. once found all weirdness disappeared ;D

package com.threed.jpct.example;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Loader;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.RGBColor;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;

/**
* A simple demo. This shows more how to use jPCT-AE than it shows how to write
* a proper application for Android. It includes basic activity management to
* handle pause and resume...
*
* @author EgonOlsen
*
*/
public class HelloWorld extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;

private Object3D ball = null;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
// back to Pixelflinger on some device (read: Samsung I7500)
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

class MyRenderer implements GLSurfaceView.Renderer {


public MyRenderer() {
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
if (fb != null) {
fb.dispose();
}

fb = new FrameBuffer(gl, w, h);


world = new World();
world.setAmbientLight(210, 210, 180);

TextureManager.getInstance().addTexture("ball", new Texture(64, 64, RGBColor.GREEN));
ball = Loader.loadSerializedObject(getClass().getResourceAsStream("ball_anim.ser"));

ball.setTransparency(-1);
ball.setTexture("ball");
ball.build();

world.addObject(ball);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 1.5f);

MemoryHelper.compact();
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

long animIndex = 0;
long lastTime = System.currentTimeMillis();

boolean firstTime = true;

public void onDrawFrame(GL10 gl) {

long now = System.currentTimeMillis();
long millis = now - lastTime;
lastTime = now;

animIndex += millis;
if (animIndex > 2000) {
animIndex = 0;
}
float anim = animIndex/2000f;
ball.animate(firstTime ? 0 : anim, 1);
firstTime = false;

fb.clear(RGBColor.BLACK);
world.renderScene(fb);
world.draw(fb);
fb.display();
}
}
}

EgonOlsen

#17
I found the reason and this version might fix it: http://jpct.de/download/beta/jpct_ae.jar...but it's a hacky fix that tries to detect this special case and applies a workaround. It's not really satisfying, but i can't figure out a better solution at the moment. The problem is that the object compiler creates vertex indices for the object based on the first frame that will be rendered. In your case, this is the first frame (index==0) which collapses multiple vertices into one due to the nature of your animation. That makes actually different vertices share the same index, which causes this polygon soup in later frames. The problem is, that i don't know which frame would be the right one to create the indices nor do i have access to that frames data even if i would know it.
This used to work in former versions, because former version didn't use indexed geometry on animated objects.

raft

thanks:) however not completely fixed. one flaw is starting from other extreme end (one) is also broken.

I've tried a quick hacky fix like this:

private static class SafeAnimated extends Object3D {

private boolean firstTime = true;

public SafeAnimated (Object3D obj) {
super(obj, true);
}

@Override
public void animate(float index, int seq) {
if (firstTime) {
firstTime = false;
if (index == 0) {
index = 0.001f;
} else if (index == 1) {
index = 0.999f;
}
}
super.animate(index, seq);
}
}

        ball = new SafeAnimated(Loader.loadSerializedObject(..));


this solved the issue in test case but not in the game. by further digging I found the reason: in game this animated ball starts invisible and gets visible only during teleporting. applied same to test case and verified that's the reason. workaround is animate ball once before anything.

a complete test case:
package com.threed.jpct.example;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import com.threed.jpct.Animation;
import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Loader;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.RGBColor;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;

/**
* A simple demo. This shows more how to use jPCT-AE than it shows how to write
* a proper application for Android. It includes basic activity management to
* handle pause and resume...
*
* @author EgonOlsen
*
*/
public class HelloWorld extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;

private Object3D ball = null;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
// back to Pixelflinger on some device (read: Samsung I7500)
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

class MyRenderer implements GLSurfaceView.Renderer {


public MyRenderer() {
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
if (fb != null) {
fb.dispose();
}

fb = new FrameBuffer(gl, w, h);


world = new World();
world.setAmbientLight(210, 210, 180);

TextureManager.getInstance().addTexture("ball", new Texture(64, 64, RGBColor.GREEN));
ball = new SafeAnimated(Loader.loadSerializedObject(getClass().getResourceAsStream("ball_anim.ser")));
ball.getAnimationSequence().setClampingMode(Animation.USE_CLAMPING);

ball.setTransparency(-1);
ball.setTexture("ball");
ball.build();

world.addObject(ball);
// ball.animate(1, 1);  // uncomment this and issue is gone

ball.setVisibility(false);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 1.5f);

MemoryHelper.compact();
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

long animIndex = 0;
long lastTime = System.currentTimeMillis();

boolean firstTime = true;
long visCountdown = -1000;

public void onDrawFrame(GL10 gl) {

long now = System.currentTimeMillis();
long millis = now - lastTime;
lastTime = now;

visCountdown += millis;
if (visCountdown > 0) {
ball.setVisibility(true);

animIndex += millis;
if (animIndex > 2000) {
animIndex = 0;
}
float anim = animIndex/2000f;
ball.animate(firstTime ? 0 : anim, 1);
firstTime = false;
}

fb.clear(RGBColor.BLACK);
world.renderScene(fb);
world.draw(fb);
fb.display();
}
}

private static class SafeAnimated extends Object3D {

private boolean firstTime = true;

public SafeAnimated (Object3D obj) {
super(obj, true);
}

@Override
public void animate(float index, int seq) {
System.out.println(index);
if (firstTime) {
firstTime = false;
if (index == 0) {
index = 0.001f;
System.out.println("--> " + index);
} else if (index == 1) {
index = 0.999f;
System.out.println("--> " + index);
}
}
super.animate(index, seq);
}
}
}


a small correction: in my case at index 0, all vertices are in 'regular' place. at index 1, they are collapsed.

EgonOlsen

I see...that's the same issue, just reversed. I've updated the jar once again with an extended hack. Maybe that one helps...if it doesn't, just stay with your workaround. The only other thing that would help in this special case is to add the option to compile to non-indexed geometry.

raft

thanks:) starting from index 1 issue is solved. however I still need to animate ball once because of late visibility thing. it's ok for me, bu I guess you should document this. I bet, even I will forgot this after some time ;)

EgonOlsen

I'm not sure how to document this...it doesn't happen with "normal" animations, just with those whose vertices collapse at some frame, which i've never seen until now. I'll add another "fix" for this but you have to enable it in Config, because it might have some side-effect on your objects if you set an Animation but doesn't apply it.

I would like to apply a proper fix for this problem, but i can't think of any ATM...i just don't know if the geometry that i'm about to render should be used as an index provider or not. In almost every case, this is just fine...but not in this special case... :(

raft

Quote from: EgonOlsen on March 21, 2013, 11:02:07 PM
it doesn't happen with "normal" animations, just with those whose vertices collapse at some frame, which i've never seen until now.
i'm not sure that's always the case for 'normal' animations. for example, closing an arm or a leg may result in collapse of some vertices

EgonOlsen

Quote from: raft on March 21, 2013, 11:10:06 PM
i'm not sure that's always the case for 'normal' animations. for example, closing an arm or a leg may result in collapse of some vertices
Yes, it might. But it's very very unlikely. I've never ever seen anything like that.

raft

Quote from: EgonOlsen on March 21, 2013, 11:02:07 PM
I'll add another "fix" for this but you have to enable it in Config, because it might have some side-effect on your objects if you set an Animation but doesn't apply it.
btw, you don't have to do that, I'm satisfied with the result ;)

EgonOlsen

I know. I consider to fix to be some kind of "documentation"... ;)