[SOLVED] Cloned Spatial local translation not translating into world translation

I have one spatial which has to be reused several times with different rotation and translation.
I tought that spatial.clone() would be good for this task, however, when I clone the spatial several times, all of them just appear at the world origin although they have all been set to diffrent local translation.

Using debugger, I have found out that local translation of cloned spatials is set, but world translation stays at 0,0,0. I have not changed anything world related at all.

Any ideas what is going on?

Is that spatial you are cloning attached to some Node (spatial.getParent() is not null) ?

No it is not attached to a node, because it only serves for cloning.

Hm, then I would need to look into the code (or someone of the core devs will answer).

Here is one idea:
You could make a call to Spatial.updateGeometricState or Spatial.forceRefresh.
Or you wait one frame, until it has been done automatically.

All of the cloning and translating and stuff happens in simpleInitApp(), so I’m not sure if that would help. Can’t test right now because I’m on mobile.

Well, it’s like that:
If you attach a Spatial to some Node (which is attached to rootNode or guiNode or customNode, or one of their child nodes) then, each frame, during update, the world coordinates and world rotation will be computed for them, during the update phase.
So, it doesn’t really make sense to clone the world coordinates, world rotation, world scale. Because these are being computed automatically and prior to that it doesn’t make sense.
At least that’s my explanation.

What are you trying to do?

I don’t need to copy the coords, rotation and stuff. I just need another spatial with the same mesh(model), but with diffrent locations and rotations.
I am basically trying to make a random dungeon generator. I have model of a straight piece of the dungeon. Then I copy it several times and try to put them to diffrent positions.
Simplest explanation: I’m trying to visualize a 2D array of dungeon pieces (only straight ones so far).

Yes, just report back if it didn’t work.
Like I said - you basically would need to attach those spatials to a Node which is either a child of the rootNode or a child of a child of the rootNode. Everything should be fine then…

I know. I attached all copies to rootNode. Everything was at 0,0,0. I attached them to a child of rootNode. Nothing changed…

Did you wait one frame or did you step in debug right there?
You would let one frame pass or call spatial.updateGeometricState right after you attached it.
Also you could try the spatial.forceRefresh after you have attached the spatial.
If both doesn’t work, I would need some example code to test it and find out the problem.
I guess it’s something simple, but try the things first which are in the first 3 sentences.
Good Luck, :chimpanzee_smile:

“I’m holding a piece of string… can you guys tell me how long it is?”

What you are experiencing is a result of something you are doing in your own code. We cannot see that code so we will blindly stab in the dark and make bad suggestions (like calling updateGeometricState()).

I recommend you put together a simple test case that illustrates the issue. 99% of the time that test case will work fine and you can use it to find your problem. 0.9% of the time you can post it here and we will spot the problem. 0.1% of the time there might be a bug in the engine in cases like this… though I doubt it here.

I have one more idea for you:
After you have attached your spatial to a rootNode’s child or child-of-child:
Just call spatial.getWorldTranslation() or spatial.getWorldRotation()
This could calculate the world coordinates from a chain of local coordinates for you.
Just try it out. :chimpanzee_smile:

How are you determining this?

How are you setting the local translation?

This is why we need to see code.

See, you say you are setting the local translation but then some crazy people actually do:
getLocalTranslation().set() which is total garbage, not actually changing the values properly, and will screw up world translation calculation.

Post code.

1 Like

I’ll post code as soon as I can get to my PC (probably on monday). Thanks for all the ideas so far.
And no I use the “correct” way using setLocalTranslation(blahblah);

It took me a while but here’s the code:
Main.java:

package dungeon;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.light.DirectionalLight;
import com.jme3.light.PointLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import dungeon.generation.DungeonGen;

public class Main extends SimpleApplication {
    PointLight light;
    private BulletAppState bulletAppState;
    
    public Spatial straightPart, bentPart;
    
    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        flyCam.setMoveSpeed(70F);
        cam.setLocation(new Vector3f(-22.92931f, 2.6491745f, -0.17925507f));
        cam.setRotation(new Quaternion(0.03900462f, 0.70585394f, -0.038985994f, 0.70620745f));
        
        /*Spatial testDungeon = assetManager.loadModel("Scenes/Dungeon.j3o");
        rootNode.attachChild(testDungeon);*/
        Node dungeonParts = new Node();
        straightPart = assetManager.loadModel("Models/dungeon_parts/straight.j3o");
        DungeonGen.Dungeon dungeon = DungeonGen.generateDungeon(3, 3, this);
        dungeon.attachToNode(dungeonParts);
        rootNode.attachChild(dungeonParts);
        
        viewPort.setBackgroundColor(ColorRGBA.Blue);
        
        light = new PointLight();
        light.setRadius(27F);
        rootNode.addLight(light);
        
        DirectionalLight light2 = new DirectionalLight();
        light2.setDirection(new Vector3f(1,0,0));
        rootNode.addLight(light2);
        
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        
        System.out.println("Root node children: " + rootNode.getChildren().size());
    }

    @Override
    public void simpleUpdate(float tpf) {
        light.setPosition(cam.getLocation());
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
}

DungeonGen.java:

package dungeon.generation;

import dungeon.generation.parts.DungeonPart;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import dungeon.Main;
import dungeon.generation.parts.StraightPart;
import java.util.Random;

/**
 *
 * @author grizeldi
 */
public class DungeonGen {    
    public static class Dungeon{
        public static final float DUNGEON_PART_WU_SIZE = 16;
        private DungeonPart[][] parts;

        public Dungeon(int xSize, int ySize) {
            parts = new DungeonPart[xSize][ySize];
        }

        public DungeonPart[][] getParts() {
            return parts;
        }
        
        public void attachToNode(Node node){
            for (int i = 0; i < parts.length; i++){
                for (int j = 0; j < parts[i].length; j++){
                    Spatial part = parts[i][j].getRepresentation();
                    System.out.println("adding a part to " + i*DUNGEON_PART_WU_SIZE + ", " + j*DUNGEON_PART_WU_SIZE);
                    part.setLocalTranslation(i * DUNGEON_PART_WU_SIZE, 0, j * DUNGEON_PART_WU_SIZE);
                    node.attachChild(part);
                }
            }
        }
        
        private void setPart(DungeonPart part, int index1, int index2){
            parts[index1][index2] = part;
        }
    }
    
    public static Dungeon generateDungeon(int xSize, int ySize, Main m){
        Dungeon dungeon = new Dungeon(xSize, ySize);
        Random random = new Random();
        
        for (int i = 0; i < xSize; i++){
            for (int j = 0; j < ySize; j++){
                switch (random.nextInt(1)){
                    case 0:
                        //Straight
                        StraightPart part;
                        if (random.nextBoolean()){
                            part = new StraightPart(m, StraightPart.ROTATION_90);
                        }else {
                            part = new StraightPart(m, StraightPart.ROTATION_NORMAL);
                        }
                        dungeon.setPart(part, i, j);
                        break;
                    case 1:
                        //Bend
                        break;
                }
            }
        }
        return dungeon;
    }
}

DungeonPart.java:

package dungeon.generation.parts;

import com.jme3.scene.Spatial;

/**
 *
 * @author grizeldi
 */
public abstract class DungeonPart {
    protected Spatial representation;

    public DungeonPart(int orientationCode) {
        initOrientation(orientationCode);
    }

    public DungeonPart() {}    
    
    public Spatial getRepresentation() {
        return representation;
    }
    
    protected abstract void initOrientation(int orientationCode);
}

And StraightPart.java:

package dungeon.generation.parts;

import com.jme3.asset.AssetManager;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import dungeon.Main;

/**
 *
 * @author grizeldi
 */
public class StraightPart extends DungeonPart{
    public static final int ROTATION_NORMAL = 0, ROTATION_90 = 1;
    private Quaternion q = new Quaternion();

    public StraightPart(Main m, int orientationCode) {
        super();
        representation = m.straightPart.clone();
    }
    
    @Override
    protected void initOrientation(int orientationCode) {
        if (orientationCode == ROTATION_90){
            representation.setLocalRotation(q.fromAngleAxis(90* FastMath.DEG_TO_RAD, Vector3f.UNIT_Y));
        }
    }
}

I have tried waiting one frame (for worldTransform to be updated), but nothing happened.

And to confirm: what is the actual problem that you are seeing?

Nothing obvious jumped out in the code but there’s a lot there that isn’t directly related to the problem. You might consider putting together a simple test case to illustrate the problem. 99% of the time that attempt will work find and then you can backtrack to figure out what’s different in your case.

In the mean time, if you print part.getWorldTranslation() here:

…what do you see?

There print world translation prints correct data. But when the jME3 window opens, all of the parts are still at 0, 0, 0 (I only see one part, but when I generate a big dungeon my FPS drops).

Then you are somehow moving them later, I guess.

No, because that is all code there is.

After you are done attaching all of the parts, iterated over all of your parts[i][j].getRepresentation() and print their world translation.