Hello Guys,
When loading a big model to the scene and scaling it down a lot (like 100 times smaller) and then attaching a camera node to it, I have a problem:
It seems that in order to see something, I need to translate the camera as if I didn’t scaled down the model. For example let’s say the model size is 100x100x100 meters and I scale it down to 1x1x1, then when attached the camera to the model’s node I need to translate it 100 meters in ordewr to see something.
here is part of the loading model function. I can send all the function code if needed. I;m creating a node, attaching the model to it (gltf based), then scale the node:
final Node model = new Node();
model.attachChild(mm); // add it to the wrapper
/////////////////////////////////////////////////
SkeletonControl skeletonCtrl = mm.getControl(SkeletonControl.class);
if(skeletonCtrl!=null && hostAppType==0) {
skeletonCtrl.setHardwareSkinningPreferred(false);// for Android 7 bug
}
final String modelName = name+"@"+modelInst.thread.threadId;
AppModel am = new AppModel(model);
am.resource = resource;
models.put(modelName,am);
model.setName(modelName);
final Vector3f scale;
if(modelInst.scaleExpr!=null) {
float sc = Float.parseFloat(modelInst.scaleExpr.evaluate(this).toString());
model.scale(sc, sc, sc);
scale = new Vector3f(sc, sc, sc);
} else {
model.scale(resource.scaleX, resource.scaleY, resource.scaleZ);
scale = new Vector3f(resource.scaleX, resource.scaleY, resource.scaleZ);
}
The easiest way to get help is reproduce the issue in self contained class we just can copy past and run. Then everything is there. This reduces the ping pong question/answer cycle to a minimum.
I do this if I have code or render issues and sometimes I find the bug by myself doing so
If you scale a node down 100 times then the children of that node will still operate in “child space”. ie: if you have a person that is 100 units tall and add a camera as a CHILD then that camera will be in 100 units tall space.
That’s the way scene graphs work.
If you want the camera to be not in “child space” then don’t put it in child space. Add our model and camera to a parent node and scale the model.
OK, I think I almost got the point
The thing is I don’t know in advance that my user will want to attach the camera to the model so I cannot add both the model & camera to the node and just then scale it.
It needs to be done dynamically.
Where the example says camNode.setLocalTranslation(new Vector3f(0, 5, -5)); , you have to supply your own start position for the camera. This depends on the size of your target (the player character) and its position in your particular scene. Optimally, you set this to a spot a bit behind and above the target.
I hacked this stuff together from wiki and the test-data library.
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.system.AppSettings;
/**
* This is the Main Class of your Game. You should only do initialization here.
* Move your Logic into AppStates or Controls
* @author normenhansen
*/
public class Main extends SimpleApplication {
public static void main(String[] args) {
AppSettings appSettings = new AppSettings(true);
appSettings.setTitle("Test Model Scale");
Main app = new Main();
app.setSettings(appSettings);
app.start();
}
@Override
public void simpleInitApp() {
// Disable the default flyby cam
flyCam.setEnabled(false);
//Set background color
viewPort.setBackgroundColor(ColorRGBA.LightGray);
/** A white, directional light source */
DirectionalLight sun = new DirectionalLight();
sun.setDirection((new Vector3f(-0.1f, -0.7f, 1.0f)).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
/** A white ambient light source. */
AmbientLight ambient = new AmbientLight();
ambient.setColor(ColorRGBA.White.mult(.2f));
rootNode.addLight(ambient);
Node parent = new Node("Parent Node");
//Add the model
Node ninja = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
parent.attachChild(ninja);
//Uncomment to scale.
// ninja.scale(.1f);
//create the camera Node
CameraNode camNode = new CameraNode("Camera Node", cam);
//This mode means that camera copies the movements of the parent:
camNode.setControlDir(ControlDirection.SpatialToCamera);
//Attach the camNode to the parent:
parent.attachChild(camNode);
//Move camNode, e.g. ahead and above the parent:
BoundingBox bounds = (BoundingBox) ninja.getWorldBound();
camNode.setLocalTranslation(0, bounds.getMax(new Vector3f()).y * 2, bounds.getMax(new Vector3f()).z *-25);
System.out.println(camNode.getLocalTranslation());
//Rotate the camNode to look at the parent:
camNode.lookAt(parent.getLocalTranslation(), Vector3f.UNIT_Y);
rootNode.attachChild(parent);
}
@Override
public void simpleUpdate(float tpf) {
//TODO: add update code
}
@Override
public void simpleRender(RenderManager rm) {
//TODO: add render code
}
}
Uncomment the scale and take note of the translation of the cam node and see that the model looks the same in both runs even though ninja is scaled.
This is just a hack specific to this model for demo. Others can chime in on a good formula for moving the camNode based on scale and translation.