Lonely spaceship (billboardnode stars not working)

I've been troubleshooting this and can't figure out what's wrong.  I'll try to summarize the problem and what I know so far:



I'm rendering randomly placed stars in a GameState.  The stars are represented by a texture on quad attached to a BillboardNode.



If I use BillboardNodes my stars don't even render until i point the camera at 0,0,0.  After they appear  as soon as I start moving the camera around they drop off the screen and only completely reappear when I look back at 0,0,0.  Using CULL_NEVER makes them work, but also kills framerate.



To figure out what was wrong I replaced my quad texture stars with spheres and was able to reproduce the problem.  If I don't use BillboardNodes everything works OK.



Screenshots and reproduction code to follow.

what boudings do you use?



starting with BillboardNodes







facing 0,0,0 with BillboardNodes







After some movement (lots of stars missing now)







Starting without BillboardNodes (behaves as expected)


package states.sky;

import com.jme.bounding.BoundingBox;
import com.jme.renderer.Renderer;
import com.jme.scene.BillboardNode;
import com.jme.scene.Node;
import com.jme.scene.SceneElement;
import com.jme.scene.SharedMesh;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;
import com.jmex.game.state.GameState;


public class SphereStarsState extends GameState {

   private Node rootNode;
   private boolean useBillboardNodes = false;
   
   public SphereStarsState() {
      
      setName("starspherestate");
      
      rootNode = new Node("root");
      
      CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
      cs.setEnabled(true);
      cs.setCullMode(CullState.CS_BACK);
      
      Node stars = new Node("starnodes");
      final float minDistance = 180f;
      final float maxDistance = 900f;
      final int maxStars = 1700;

      Sphere starSphere = new Sphere("starsphere",5,5,1.0f);
      starSphere.setLightCombineMode(LightState.OFF);
      
      for(int i =0; i < maxStars; i ++) {
         
         SharedMesh sharedStar = new SharedMesh("shared_star",starSphere);
      
         sharedStar.setCullMode(SceneElement.CULL_DYNAMIC);
         sharedStar.setRenderState(cs);
         sharedStar.setLocalScale(1f);
         sharedStar.setModelBound(new BoundingBox());
         sharedStar.updateModelBound();
         sharedStar.updateWorldBound();
         sharedStar.updateGeometricState(0.0f, true);
         sharedStar.updateRenderState();
         
         float x=0,y=0,z=0;
         
         while(com.jme.math.FastMath.sqrt(x*x+y*y+z*z) > maxDistance || com.jme.math.FastMath.sqrt(x*x+y*y+z*z) < minDistance) {
         
            x = (float)Math.random()*-maxDistance+(float)Math.random()*maxDistance;
            y = (float)Math.random()*-maxDistance+(float)Math.random()*maxDistance;
            z = (float)Math.random()*-maxDistance+(float)Math.random()*maxDistance;
         
         }
         
         if (useBillboardNodes) {
            BillboardNode b = new BillboardNode("star_"+i);
            b.setAlignment(BillboardNode.CAMERA_ALIGNED);
            b.attachChild(sharedStar);
            b.setLocalTranslation(x,y,z);
            b.setRenderQueueMode(Renderer.QUEUE_SKIP);
            stars.attachChild(b);
         }
         else {
            sharedStar.setLocalTranslation(x,y,z);
            stars.attachChild(sharedStar);
         }
         
      }

      stars.updateRenderState();

      rootNode.attachChild(stars);
      
      //rootNode.setCullMode(SceneElement.CULL_NEVER);
      rootNode.setRenderQueueMode(Renderer.QUEUE_SKIP);
      rootNode.setRenderState(cs);      
      rootNode.updateGeometricState(0.0f, true);
      rootNode.updateRenderState();
      
   }
   
   @Override
   public void cleanup() {
      // TODO Auto-generated method stub
      
   }

   @Override
   public void render(float arg0) {
      // TODO Auto-generated method stub
      DisplaySystem.getDisplaySystem().getRenderer().draw(rootNode);
   }

   @Override
   public void update(float arg0) {
      // TODO Auto-generated method stub
      rootNode.setLocalTranslation( DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation());
      rootNode.updateGeometricState(arg0, true);
      
   }

   
   
}

It seems this is a bug in BillboardNode. Boundings are not computed correctly. BillboardNode overrides updateWorldData preventing it's children from getting world vectors updated, after that the world bounds can't be computed correctly. This obviously was a performance optimization, done a long time ago (in 2005).



Replacing updateWorldBound(); in BillboardNode.updateWorldData with super.updateWorldData( time ); solves your problem.



@other devs: should this go into cvs, or is anyone up to fixing this without loosing the optimization?

Thanks for looking at this irrisor!  :smiley:

I have committed this unoptimized fix as no volunteers jumped in

Seems ok.  Probably the reason for optimizing it has passed.