Node size relative to the camera

Hi,



how can I make my billboard node scales relative to the camera? I mean, like the node that rotates relative to the camera (using BillboardControl) I need to make my billboard node “scale invariant”, so no matters how far or close the camera is it will be always the same size. Maybe a “ScaleControl” attached to the node (just guessing)?







Objective: Display some markers/pins (like google maps) over my map so the user must always see it at the same size.





Thanks.

I was interested myself and came up with this:



[java]package com.tests;



import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Sphere;



public class TestKeepObjectSameSize extends SimpleApplication {



public static void main(String[] args) {

new TestKeepObjectSameSize().start();

}

private Geometry mark;



private final float RADIUS = 1f;



@Override

public void simpleInitApp() {

flyCam.setMoveSpeed(20);



Sphere sphere = new Sphere(30, 30, RADIUS);

mark = new Geometry(“mark”, sphere);

Material mark_mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

mark_mat.setColor(“Color”, ColorRGBA.Red);

mark.setMaterial(mark_mat);

rootNode.attachChild(mark);

}



@Override

public void simpleUpdate(float tpf) {

Vector3f camPosition = cam.getLocation();

Vector3f markPos = mark.getLocalTranslation();



float scale;



if (markPos.length() == 0) {

scale = camPosition.length();

} else {

scale = camPosition.length() / markPos.length();

}

mark.setLocalScale(scale * RADIUS * 0.25f);

}

}[/java]



Its not perfect as you have to add a factor to readjust the scale, which I just hardcoded based on the radius of the sphere and some made up factor. I assume it is calculate-able somehow, but I don’t have time to perfect it



Perhaps using the Gui node and Cam.getScreenCoordinates () width a fixed width/height may be more appropriate idk

Thanks @wezrule,



but using your code the object still changing the size when the camera go far/near…



So, using some hints that you gave me on your code I’ve written this control to add to the Node:



[java]

import java.io.IOException;



import com.jme3.export.InputCapsule;

import com.jme3.export.JmeExporter;

import com.jme3.export.JmeImporter;

import com.jme3.export.OutputCapsule;

import com.jme3.math.Vector3f;

import com.jme3.renderer.Camera;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.scene.Spatial;

import com.jme3.scene.control.AbstractControl;



public class KeepSizeControl extends AbstractControl {



private float radius;



public KeepSizeControl() {

super();

radius = 1f;

}



@Override

protected void controlUpdate(float tpf) {

}



@Override

protected void controlRender(RenderManager rm, ViewPort vp) {

Camera cam = vp.getCamera();

fixSize(cam);

}



private void fixRefreshFlags() {

// force transforms to update below this node

spatial.updateGeometricState();



// force world bound to update

Spatial rootNode = spatial;

while (rootNode.getParent() != null) {

rootNode = rootNode.getParent();

}



rootNode.getWorldBound();

}



private void fixSize(Camera cam) {

Vector3f camPosition = cam.getLocation();

Vector3f nodePosition = spatial.getLocalTranslation();



float scale = camPosition.distance(nodePosition);



spatial.setLocalScale(scale * radius * 0.0001f);

fixRefreshFlags();

}



public void setRadius(float radius) {

this.radius = radius;

}



public float getRadius() {

return radius;

}



@Override

public void write(JmeExporter e) throws IOException {

super.write(e);

OutputCapsule capsule = e.getCapsule(this);

capsule.write(radius, “radius”, 0f);

}



@Override

public void read(JmeImporter e) throws IOException {

super.read(e);

InputCapsule capsule = e.getCapsule(this);

radius = capsule.readFloat(“radius”, 0f);

}

}

[/java]





Using it:

[java]

KeepSizeControl ksc = new KeepSizeControl();

ksc.setRadius(5f);

targetNode.addControl(ksc);

[/java]





Is this correct? Its working now, but i would like to hear the opinion of some more experienced user.



Thanks,

@tiago156 said:
Thanks @wezrule,

but using your code the object still changing the size when the camera go far/near...


rly? cus it doesn't on mine

I’d do like this

[java]

float FACTOR = 1f;



float scale = camPosition.subtract(markPos).length() / 100f * FACTOR;

object.setLocalScale(scale);[/java]



You can set the factor to whatever you want! If the object is getting vanished in the screen, it’s because of the far plane (I’m almost sure!).

Its working now, thanks.



[java]

import java.io.IOException;



import com.jme3.export.InputCapsule;

import com.jme3.export.JmeExporter;

import com.jme3.export.JmeImporter;

import com.jme3.export.OutputCapsule;

import com.jme3.math.Vector3f;

import com.jme3.renderer.Camera;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.scene.Spatial;

import com.jme3.scene.control.AbstractControl;



public class KeepSizeControl extends AbstractControl {



private float factor;



public KeepSizeControl() {

super();

factor = 1f;

}



@Override

protected void controlUpdate(float tpf) {

}



@Override

protected void controlRender(RenderManager rm, ViewPort vp) {

Camera cam = vp.getCamera();

fixSize(cam);

}



private void fixRefreshFlags() {

// force transforms to update below this node

spatial.updateGeometricState();



// force world bound to update

Spatial rootNode = spatial;

while (rootNode.getParent() != null) {

rootNode = rootNode.getParent();

}



rootNode.getWorldBound();

}



private void fixSize(Camera cam) {

Vector3f camPosition = cam.getLocation();

Vector3f nodePosition = spatial.getLocalTranslation();



float scale = camPosition.subtract(nodePosition).length() / 100f

  • factor;



    spatial.setLocalScale(scale);

    fixRefreshFlags();

    }



    public void setFactor(float factor) {

    this.factor = factor;

    }



    public float getFactor() {

    return factor;

    }



    @Override

    public void write(JmeExporter e) throws IOException {

    super.write(e);

    OutputCapsule capsule = e.getCapsule(this);

    capsule.write(factor, "factor", 0f);

    }



    @Override

    public void read(JmeImporter e) throws IOException {

    super.read(e);

    InputCapsule capsule = e.getCapsule(this);

    factor = capsule.readFloat("factor", 0f);

    }

    }

    [/java]