vertex controller problem.(Solved)

Started by davec64, November 13, 2013, 12:39:22 PM

Previous topic - Next topic

davec64

I am having an issue applying a vertex controller to a plane. Once I have added it to the world.
I update the plane in an async task it works fine the first time (this is done  just before i add the object to the world), but the second time even though it succeeds there is no visible change to my plane.
Is there anything else I need to do to get the new mesh to appear  instead of the old mesh.
I tried touch but that did nothing.
My next test is to apply a second vertex before I add the object to the world.
Any thoughts or explanation on why the second apply does not work .lazy transformations are off.
Dave

EgonOlsen

If you are using compiled object (or jPCT-AE), make sure to attach the controller before calling build() or otherwise, the object will be compiled to static geometry and won't reflect any changes to the mesh.

davec64

#2
Thanks for the prompt response. How ever that seems worse. I loose the plane altogether if I wait for the vertex controller. here is the relevant code it is on AE
I hope it is something stupid.  ;)


    m_channel = Primitives.getPlane(PLANESIZE, 10);
      m_channel.setAdditionalColor(new RGBColor(0, 0, 0));
      m_channel.rotateX(PI / 2);
      m_channel.setSpecularLighting(true);
      TextureInfo ti = new TextureInfo(TextureManager.getInstance().getTextureID("channel"));
      m_channel.setTexture(ti);
      reTexture(m_channel, "channel", 5f);
      m_channel.disableVertexSharing();
      // enabling lazy transforms means do not transform this object in the future. Ok for the water
      // but nothing else
      m_channel.forceGeometryIndices(true);
      m_channel.setCulling(true);

      // Deform the channel do this as a background task
      // make sure to attach the controller before calling build() or otherwise, the object will be
      // compiled to static geometry and won't reflect any changes to the mesh.
      m_channelMesh = m_channel.getMesh();
      m_controller = new Mod();
      m_channel.build();
      UpdateMapTask task = new UpdateMapTask(bathyfiles, m_controller, m_channelMesh, m_channel, m_shipGpsLat,
          m_shipGpsLong);
      task.execute();
      while (MainActivity.m_UpdateinProgress){
        try {
          Thread.sleep(5);
        }
        catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      m_world.addObject(m_channel);


and the async task

  private final static String m_tag = "UpdateMapDataTask";
  private BathyFiles bathyFiles;
  private GenericVertexController controller;
  private Mesh channelMesh;
  private Object3D channel; 
  private double shipgpslong, shipgpslat;

  /**
   * This task is called to pre fetch the next map data
   *
   * @param m_controller
   */
  public UpdateMapTask(BathyFiles bathyfiles, GenericVertexController m_controller,
      Mesh m_channelMesh, Object3d m_channel, double m_shipGpsLat, double m_shipGpsLong) {
    bathyFiles = bathyfiles;
    controller = m_controller;
    channelMesh = m_channelMesh;
    shipgpslong = m_shipGpsLong;
    shipgpslat = m_shipGpsLat;
    channel=m_channel;
  }

  @Override
  protected void onPreExecute() {
    MainActivity.m_UpdateinProgress = true;
  }

  @Override
  protected String doInBackground(Void... params) {
    List<Tile> tiles = bathyFiles.getTileset(shipgpslong, shipgpslat);
    Logger.log(m_tag + " updating map to new coords " + shipgpslat + " lat " + shipgpslong
        + " long");
    bathyFiles.buildMapWindow(tiles, shipgpslong, shipgpslat);
    boolean success = channelMesh.setVertexController(controller, false);
    Logger.log(m_tag + " success " + success);
    channelMesh.applyVertexController();
    channel.touch();
    return null;
  }

  @Override
  protected void onPostExecute(String test) {
    MainActivity.m_UpdateinProgress = false;
    Logger.log(m_tag + " finished updating map to new coords " + shipgpslat + " lat " + shipgpslong
        + " long");
  }


After trying for a few hours to work this out the only way I got it to work sort of was to create a new plane object delete the old object from the world and then add the new plane. This will let me continue working for now but I really need to know what is wrong.  My plane mesh is quite large so I don't want to keep allocating and de allocating. It does take a long time to do that. without recreating the new plane it only takes .5 of a second for my code to complete. with the new object it takes over 10 seconds.
Dave

EgonOlsen

You aren't calling touch() (at least i can't spot it in the code) after applying the controller and your way to build() the object looks really strange to me. Why are you constantly calling build() while the update thread does it's work? That doesn't make sense nor will be results be very pleasing because it will build and compile some inbetween state.

EgonOlsen

touch() is mandatory. Put it back in or you'll never see any changes. About that build()-calls: I understand what you want to do but that's not what you are doing. What you actually want is more something like


while (MainActivity.m_UpdateinProgress) {
Thread.sleep(5);
}
m_channel.build();


I'm not sure ATM if i have a stripped down example for jPCT-AE. I have one for desktop jPCT, but that slightly differs from what you would have to do on Android.

davec64

#5
I have updated the code snippet above to show the changes you recommend. It now displays the first planemesh as it should.
However when I try to update the mesh nothing happens it is as if it is compiled to a static geometry. As you suggested in your first response.
If I do not apply the mesh in the first updatetask the mesh stays flat as it should. then the next time I call the updatetask with a flag to update the mesh. it still stays flat.
putting the build just before I add the plane to the world does not display the plane.
Dave



EgonOlsen

Try to force dynamic compilation by adding a plane.compile(true); before the build(). If that still doesn't help, maybe you can upload the project for me (or a simplified test case), so that i can have a look!?

davec64

Yep I tried that just in case.
I will send you a simplified test case as soon as I can.

EgonOlsen

I made a test case myself and it works fine...maybe it helps to find the problem:


package com.example.hellowater;

import java.lang.reflect.Field;

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 android.view.MotionEvent;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.GenericVertexController;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;

public class WaterActivity extends Activity {

private static WaterActivity master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D water = null;
private WaterController wc = null;
private int fps = 0;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

if (master != null) {
copy(master);
}

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

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
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();
}

@Override
protected void onStop() {
super.onStop();
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();

public MyRenderer() {
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
fb = new FrameBuffer(gl, w, h);

if (master == null) {
world = new World();
world.setAmbientLight(100, 100, 100);

water = Primitives.getPlane(20, 3);
wc = new WaterController();

water.rotateX((float) Math.PI / 2f);
water.rotateMesh();
water.clearRotation();
water.getMesh().setVertexController(wc, false);
water.build();
world.addObject(water);

world.getCamera().setPosition(0, -50, -50);
world.getCamera().lookAt(water.getTransformedCenter());

Light light = new Light(world);
light.setAttenuation(-1);
light.setIntensity(255, 255, 255);
light.setPosition(new SimpleVector(100, -50, -20));

MemoryHelper.compact();

if (master == null) {
Logger.log("Saving master Activity!");
master = WaterActivity.this;
}
}
}

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

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
water.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
water.rotateX(touchTurnUp);
touchTurnUp = 0;
}

fb.clear(back);

wc.update(0.5f);
water.getMesh().applyVertexController();
water.touch();

world.renderScene(fb);
world.draw(fb);
fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}

private static class WaterController extends GenericVertexController {

private static final long serialVersionUID = 1L;

private float degreeAdd = 0;

public void update(float inc) {
degreeAdd += inc;
}

public void apply() {
SimpleVector[] source = this.getSourceMesh();
SimpleVector[] dest = this.getDestinationMesh();

int end = source.length;

for (int i = 0; i < end; i++) {
SimpleVector s = source[i];
SimpleVector d = dest[i];
float sin = (float) Math.sin((((((degreeAdd + s.x + s.z) / 10f) % 360))));
d.set(s.x, s.y + sin * 2, s.z);
}
}
}

}


davec64

Hi Thanks for the reply and example.
It still did not work as expected but it did give me a good idea of where I was going wrong. Here is my final working code for any one else who has this issue.


     m_channel = Primitives.getPlane(PLANESIZE, 10);
      m_channel.setAdditionalColor(new RGBColor(0, 0, 0));
      m_channel.rotateX(PI / 2);
      // m_channel.rotateMesh();
      // m_channel.clearRotation();
      m_channel.setSpecularLighting(true);
      TextureInfo ti = new TextureInfo(TextureManager.getInstance().getTextureID("channel"));
      m_channel.setTexture(ti);
      reTexture(m_channel, "channel", 5f);
      m_channel.disableVertexSharing();
      // enabling lazy transforms means do not transform this object in the future. Ok for the water
      // but nothing else
      // m_channel.forceGeometryIndices(true);
      m_channel.setCulling(true);
      m_channelController = new ChannelController();

      // Calc Normals must be done now otherwise the plane becomes a black texture.
      m_channel.calcNormals();

      // make sure to attach the controller before calling build() or otherwise, the object will be
      // compiled to static geometry and won't reflect any changes to the mesh.
      m_channel.getMesh().setVertexController(m_channelController, false);
      m_channel.build();
      // Deform the channel do this as a background task
      UpdateMapTask task = new UpdateMapTask(bathyfiles, m_channel, m_shipGpsLat, m_shipGpsLong);
      task.execute();
       m_world.addObject(m_channel);



and in the async background task

@Override
  protected String doInBackground(Void... params) {
    List<Tile> tiles = bathyFiles.getTileset(shipgpslong, shipgpslat);
    Logger.log(m_tag + " updating map to new coords " + shipgpslat + " lat " + shipgpslong
        + " long");
    bathyFiles.buildMapWindow(tiles, shipgpslong, shipgpslat);
    channel.getMesh().applyVertexController();
    channel.touch();
    return null;
  }

So the issue was no normals had been calculated when I ported from your example because I had textures.
The root cause was I was doing this more than once.
channelMesh.setVertexController(controller, false);
which I think was breaking (creating a new link) to the mesh.
Dave