Author Topic: Displaying Object3D with VertexController and billboards  (Read 4660 times)

Offline Wojtek

  • int
  • **
  • Posts: 62
    • View Profile
Displaying Object3D with VertexController and billboards
« on: April 21, 2010, 01:22:30 am »
Hello,

Recently I have found strange behavior.
I have a class that allows me to create simple Rays. It uses VertexController to change ray dimensions which gives me possibility to reuse objects. I use it for laser or ship fumes effects.

Now I have other objects displayed as billboards - planets, stars etc.

I have noticed recently, that scaled billboard is displayed sometimes over the ray despite fact that ray is much closer to camera.

There are screensots of simple app where ray is from [-20,0,-3] to [0,-20,-3] and box of size (2,2) is located at [-150,-150,-40]:

The billboard does not have scale applied.

A scale is applied ( box.setScale(6); ).


Here is an example application:
//Test.java
Code: [Select]
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

public class Test extends JFrame implements Runnable
{
    private static final long serialVersionUID = 811111147457394977L;
    private World world;
    private FrameBuffer buffer;
    private Canvas myCanvas;
    private boolean alive = true;
    private boolean initialized = false;
    private Ray ray;
    private Object3D box;

    public static void main(String[] args)
    {
new Test();
    }

    public Test()
    {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
pack();
setVisible(true);
    }

    public void init()
    {
world = new World();
ray = new Ray();

ray.initialize(new SimpleVector(-20, 0, -3), new SimpleVector(0, -20,
-3), 0.5, Color.YELLOW, Object3D.TRANSPARENCY_MODE_DEFAULT);
box = Primitives.getBox(2, 2);

box.setBillboarding(true);
box.setAdditionalColor(Color.WHITE);
box.setLighting(Object3D.LIGHTING_NO_LIGHTS);
box.setTransparency(0);
box.setTransparencyMode(Object3D.TRANSPARENCY_MODE_ADD);
box.setTransparency(255);
box.setScale(6);
box.build();
box.compile();
box.translate(-150, -150, -40);

world.addObject(ray);
world.addObject(box);

Camera camera = world.getCamera();
camera.setPosition(0, 0, 0);
camera.lookAt(box.getTransformedCenter());
world.setAmbientLight(100, 100, 100);

buffer = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_GL_AA_4X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);

myCanvas = buffer.enableGLCanvasRenderer();
add(myCanvas, BorderLayout.CENTER);
initialized = true;
new Thread(this).start();
    }

    @Override
    public void run()
    {
while (alive)
{
    this.repaint();
    try
    {
Thread.sleep(1000);
    }
    catch (InterruptedException e)
    {
    }
}
alive = false;
    }

    @Override
    public void paint(Graphics g)
    {
if (!initialized)
    return;
buffer.clear();
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
myCanvas.repaint();

    }
}

//Ray.java
Code: [Select]
public class Ray extends Object3D
{
    private static final long serialVersionUID = -1684665440334869826L;
    private static final SimpleVector YVEC = new SimpleVector(0, 1, 0);
    private static final SimpleVector ZVEC = new SimpleVector(0, 0, 1);
    private static int HF1 = 0;
    private static int HF2 = 1;
    private static int HT1 = 2;
    private static int HT2 = 3;
    private static int VF1 = 4;
    private static int VF2 = 5;
    private static int VT1 = 6;
    private static int VT2 = 7;
    private static SimpleVector[] VERTEXES = new SimpleVector[] {
    new SimpleVector(-1.5, -1.5, -1), new SimpleVector(-0.5, -0.5, -1),
    new SimpleVector(1.5, 1.5, -1), new SimpleVector(0.5, 0.5, -1),
    new SimpleVector(-1, -1, -1.5), new SimpleVector(-0.5, -0.5, -0.5),
    new SimpleVector(1.5, 1.5, 1.5), new SimpleVector(0.5, 0.5, 0.5) };
    private SimpleVector[] vertexes = new SimpleVector[VERTEXES.length];

    public Ray()
    {
super(4);

for (int i = 0; i < VERTEXES.length; ++i)
    vertexes[i] = new SimpleVector(VERTEXES[i]);

addTriangle(VERTEXES[HF1], 1, 1, VERTEXES[HT1], 1, 0, VERTEXES[HT2], 0,
    0);
addTriangle(VERTEXES[HT2], 0, 0, VERTEXES[HF2], 0, 1, VERTEXES[HF1], 1,
    1);
addTriangle(VERTEXES[VF1], 1, 1, VERTEXES[VT1], 1, 0, VERTEXES[VT2], 0,
    0);
addTriangle(VERTEXES[VT2], 0, 0, VERTEXES[VF2], 0, 1, VERTEXES[VF1], 1,
    1);

setCulling(Object3D.CULLING_DISABLED);
setLighting(Object3D.LIGHTING_NO_LIGHTS);
setTransparency(0);
build();
getMesh().setVertexController(new RayVertexController(),
    IVertexController.PRESERVE_SOURCE_MESH);
    }

    public void initialize(SimpleVector from, SimpleVector to, double scale, Color color,
    int transparencyMode)
    {
setVertexes(from, to, (float) scale);
setAdditionalColor(color);
setTransparencyMode(transparencyMode);
getMesh().applyVertexController();
setCenter(SimpleVector.ORIGIN);
setOrigin(SimpleVector.ORIGIN);
getRotationMatrix().setIdentity();
setRotationPivot(SimpleVector.ORIGIN);
    }

    private void setVertexes(SimpleVector from, SimpleVector to, float scale)
    {
scale /= 2;

SimpleVector diff = to.calcSub(from);
SimpleVector ya = SimpleVectorUtil.mul(
    diff.calcCross(YVEC).normalize(), scale);
SimpleVector za = SimpleVectorUtil.mul(
    diff.calcCross(ZVEC).normalize(), scale);

vertexes[HF1].set(from.x - ya.x, from.y - ya.y, from.z - ya.z);
vertexes[HF2].set(from.x + ya.x, from.y + ya.y, from.z + ya.z);
vertexes[HT1].set(to.x - ya.x, to.y - ya.y, to.z - ya.z);
vertexes[HT2].set(to.x + ya.x, to.y + ya.y, to.z + ya.z);
vertexes[VF1].set(from.x - za.x, from.y - za.y, from.z - za.z);
vertexes[VF2].set(from.x + za.x, from.y + za.y, from.z + za.z);
vertexes[VT1].set(to.x - za.x, to.y - za.y, to.z - za.z);
vertexes[VT2].set(to.x + za.x, to.y + za.y, to.z + za.z);
    }

    public SimpleVector convertVertex(SimpleVector vertex)
    {
for (int i = 0; i < VERTEXES.length; ++i)
    if (VERTEXES[i].equals(vertex))
return vertexes[i];
return vertex;
    }

    class RayVertexController extends GenericVertexController
    {
private static final long serialVersionUID = -3596694596175935772L;

@Override
public void apply()
{
    SimpleVector[] src = getSourceMesh();
    SimpleVector[] dst = getDestinationMesh();
    for (int i = 0; i < src.length; ++i)
    {
dst[i].set(convertVertex(src[i]));
    }
}
    }
}

//SimpleVectorUtil.java
Code: [Select]
public class SimpleVectorUtil
{

    public static SimpleVector mul(SimpleVector vec, float mul)
    {
vec.x *= mul;
vec.y *= mul;
vec.z *= mul;
return vec;
    }

    public static SimpleVector calcMul(SimpleVector vec, float mul)
    {
return mul(new SimpleVector(vec), mul);
    }
}

Is it a bug, or perhaps I have to set some additional parameters to have that working correctly?

Thanks,
Wojtek

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Displaying Object3D with VertexController and billboards
« Reply #1 on: April 21, 2010, 10:48:46 am »
It's not a bug, it's a problem with sorting. Transparent objects get sorted according to their depth. However, this relies on the so called Painter's algorithm and that one isn't exact for every scene, because it takes the average depth on a polygon or, for compiled objects, the depth of the object's center. And just because the center is in front, this doesn't mean that the whole object is in front, but it will be drawn as if resulting in visual flaws. If you can ensure that one kind of transparent object is always in front (or behind) any other kind of transparent object, you can play around with Object3D.setSortOffset(). However, in this case, this may not be sufficient...you can try to tessellate the line, so that it consists on more but smaller objects to improve the sorting...if that's an option.
« Last Edit: April 21, 2010, 01:48:53 pm by EgonOlsen »

Offline Wojtek

  • int
  • **
  • Posts: 62
    • View Profile
Re: Displaying Object3D with VertexController and billboards
« Reply #2 on: April 21, 2010, 08:30:16 pm »
But why it works for not billboarded objects? Ie. when I comment out a following line
Code: [Select]
box.setBillboarding(true); the screen is rendered properly even if I set much bigger scale?

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Displaying Object3D with VertexController and billboards
« Reply #3 on: April 21, 2010, 09:11:33 pm »
Most likely because the billboarding has an influence on the transformation of the center from object to world space. I'll look into it in more detail and report back...

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Displaying Object3D with VertexController and billboards
« Reply #4 on: April 21, 2010, 09:43:50 pm »
What i said above is all true...however, this wasn't the problem here. The problem was, that the calculation of the depth value for transparent objects was utter nonsense. It should be fixed in this jar: http://www.jpct.net/download/beta/jpct.jar. Please give it a try.

Offline Wojtek

  • int
  • **
  • Posts: 62
    • View Profile
Re: Displaying Object3D with VertexController and billboards
« Reply #5 on: April 21, 2010, 10:09:41 pm »
Thank you. Now it works much better :)

Wojtek