Save model .j3o and "normalize" scale to 1

I would like to save a model to a .j3o but making his actual scale as it was a 1 scale (Transform/modify it).

For example, my model height and width are 4 and 4 units. I scale it to 0.5 (now iis 2x2 units and it scale value is 0.5), I save it.
When I load it I would like to have the model at 2x2 units (that’s right) but I want it to have a scale value of 1 (it’s 0.5).

Is there a way to “normalize” the scale or stablish that it actuals dimensions are fixed to scale 1?.

You could simplify this by just using:

[java]
someModel.setLocalScale(someModel.getLocalScale().mult(Vector3f.UNIT_XYZ.divide(someModel.getLocalScale())));
[/java]

Actually… the above assume scaling from the beginning… sorry.

You would want to replace getLocalScale() with deciding on a reference axis and getting the bounds along that axis. Then mult by the scale factor.

But the real question is, are you wanting to alter the scale? Or are you wanting to actually modify the original buffer?

Wouldn’t that be just like put?:

someModel.setScale(1f);

What I really want is to set that the actual width and height (the actual dimension) of the model is scale 1. If the actual width and height are 2x2 (and it is on 0.5f scale) I want it to be scale 1f but stay on 2x2. Something like:

someModel.setScaleFixed(1f);

So setScaleFixed(1f) set the model scale to 1f but it doesn’t change it dimensions.

I just would like to change the model dimension but stay having it “scale” .

@NemesisMate said: Wouldn't that be just like put?:

[java]someModel.setScale(1f);[/java]

What I really want is to set that the actual width and height (the actual dimension) of the model is scale 1. If the actual width and height are 2x2 (and it is on 0.5f scale) I want it to be scale 1f but stay on 2x2. Something like:

[java]someModel.setScaleFixed(1f);[/java]

So setScaleFixed(1f) set the model scale to 1f but it doesn’t change it dimensions.

I just would like to change the model dimension but stay having it “scale” .

For the first question, no. This would simply set the scale to 1 (which in your above example is 4 world units). If you want to reduce the actual size, you would have to do something along the lines of

[java]
float h = someModel.whateverMethodJMEProvidesToGetBoundHeight();
float scale = 1/h;
someModel.setLocalScale(someModel.getLocalScale().mult(scale));
[/java]

This would set the scale to some fraction of itself (assuming the model is larger of course)

Though, I’m not sure what implications scaling has on translation, etc.

Are you planning on resizing these models after normalizing them? The reason I ask, is if this is a one-time conversion… I would suggest actually pulling the position buffer and scaling each vertices position. This way the actually model in a height of 1 (or whichever axis you determine is dominate) and then you don’t need to worry about the effect scaling may have later.

What I’m planning is to load external models and allow to resize them on a “game editor” so anyone can load a model, scale it to get it fit on my world, and save it with it great size ready to be loaded on the game. But I want them to have scale 1 to don’t have strange behaviours and aside effects on other “things” that are attached later to them. What you suggest… I think is too hard for my knowledge on graphics stuffs so I suppose there isn’t an easy way.

Whatever, thanks for all :wink:

@NemesisMate said: What I'm planning is to load external models and allow to resize them on a "game editor" so anyone can load a model, scale it to get it fit on my world, and save it with it great size ready to be loaded on the game. But I want them to have scale 1 to don't have strange behaviours and aside effects on other "things" that are attached later to them. What you suggest... I think is too hard for my knowledge on graphics stuffs so I suppose there isn't an easy way.

Whatever, thanks for all :wink:

Actually, modifying the position buffer is very simple and it will use the same method I posted above.

// Assuming you are doing this from the mesh class (i.e. methods below are direct calls internal to Mesh.java)
[java]
VertexBuffer pb = getBuffer(VertexBuffer.Type.Position);
FloatBuffer positions = (FloatBuffer) pb.getData();

for (int i = 0; i < positions.capacity(); i ++) {
positions.put(i, positions.get(i) * scale);

}

pb.updateData(positions);
[/java]

Anyways… this is written from memory… so it may not be exact, however… it is the basic idea. Very simple to update the mesh as needed.

1 Like

Oh, thanks, I tried it and the position seems to be well updated internally… but there isn’t any visual size change on the model (It dimensions still the same :S)

SceneGraphVisitor visitor = new SceneGraphVisitor()  {
    @Override
    public void visit(Spatial spat) {
        if(spat instanceof Geometry) {
            VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
            FloatBuffer positions = (FloatBuffer) pb.getData();

            for(int i = 0; i &lt; positions.capacity(); i++)
                    positions.put(i, positions.get(i) * scale);

                pb.updateData(positions);
            }
        }
};

spatial.depthFirstTraversal(visitor);
```

That's just the code I have (I tried to print one of the pb points before and after the change and it's well scaled).

Ok… since you are updating this from outside the Mesh class, try updating it like:

[java]

((Geometry)spat).getMesh().clearBuffer(Type.Position);
((Geometry)spat).getMesh().setBuffer(Type.Position, 3, positions);
((Geometry)spat).getMesh().createCollisionData();
((Geometry)spat).updateModelBounds(); // updateModelBound()… one of the two
[/java]

I tried to put what you said but it still without visual changes, the model still looking the same.

SceneGraphVisitor visitor = new SceneGraphVisitor() {
    @Override
    public void visit(Spatial spat) {
        if(spat instanceof Geometry) {
            VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
            FloatBuffer positions = (FloatBuffer) pb.getData();

            for(int i = 0; i &lt; positions.capacity(); i++)
                positions.put(i, positions.get(i) * scale);

            ((Geometry)spat).getMesh().clearBuffer(Type.Position);
            ((Geometry)spat).getMesh().setBuffer(Type.Position, 3, positions);
            ((Geometry)spat).getMesh().createCollisionData();
            ((Geometry)spat).updateModelBound();
//            pb.updateData(positions); // tried with and without this, the same result
        }
    }
};

spatial.depthFirstTraversal(visitor);
@NemesisMate said: I tried to put what you said but it still without visual changes, the model still looking the same.

[java]SceneGraphVisitor visitor = new SceneGraphVisitor() {
@Override
public void visit(Spatial spat) {
if(spat instanceof Geometry) {
VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
FloatBuffer positions = (FloatBuffer) pb.getData();

        for(int i = 0; i &lt; positions.capacity(); i++)
            positions.put(i, positions.get(i) * scale);

        ((Geometry)spat).getMesh().clearBuffer(Type.Position);
        ((Geometry)spat).getMesh().setBuffer(Type.Position, 3, positions);
        ((Geometry)spat).getMesh().createCollisionData();
        ((Geometry)spat).updateModelBound();

// pb.updateData(positions); // tried with and without this, the same result
}
}
};[/java]

spatial.depthFirstTraversal(visitor);

You said earlier that you println’d the altered position. The output definitely showed a scaled position, yes?

In the code above, I would think you would need to determine the scale as this would change for each Geometry. What do you have scale set to? Or how are you determining what it is per Geometry?

The “scale” variable is a final one setted by method. It is modified “dinamically” so I can set the new scale to the value I want, i.e: 0.5. If I call that method with scale = 0.5, the position buffer seems to scale it internal values (if I print any position before and after that code, I can see the value = value * scale). But I would like to see a visual effect, I would like to have that model resized to the scale value (Obviously, maintaining the scale 1).

The full code is just:

public void resize(final float scale) {
    //-- The code above --//
    spatial.depthFirstTraversal(visitor);
}

Here, spatial is the node with my model geometries.

@NemesisMate said: The "scale" variable is a final one setted by method. It is modified "dinamically" so I can set the new scale to the value I want, i.e: 0.5. If I call that method with scale = 0.5, the position buffer seems to scale it internal values (if I print any position before and after that code, I can see the value = value * scale). But I would like to see a visual effect, I would like to have that model resized to the scale value (Obviously, maintaining the scale 1).

The full code is just:

[java]public void resize(final float scale) {
//-- The code above --//
spatial.depthFirstTraversal(visitor);
}[/java]

Here, spatial is the node with my model geometries.

Do me a favor to help test where this is failing.

  1. Create a new app
  2. In simpleInit load the same model twice and place them side by side
  3. Copy & paste the above code that changes the position buffer (just the needed bits) into simpleInit and alter only one of the models.
  4. Add them to your scene and verify that the buffer update is working correctly.

It seems to be a model problem. If I “resize” a Box (from com.jme3.scene.shape.Box) it resizes the box great but if I try with a loaded .xml (an Ogre Mesh) it doesn’t resize anything(only internal values).

I tried with Sinbad(On normen’s Quixote demo: https://dl.dropbox.com/u/73678281/SDK-DemoCode/SDK-UsecaseDemo_1.zip) and the ninja (on the test-data library), both with failure.

@pspeed You are the mesh madman. Any idea why the above would work with some meshes and not others?

Basically it alters the position buffer to normalize model sizes without scaling.

@NemesisMate said: It seems to be a model problem. If I "resize" a Box (from com.jme3.scene.shape.Box) it resizes the box great but if I try with a loaded .xml (an Ogre Mesh) it doesn't resize anything(only internal values).

I tried with Sinbad(On normen’s Quixote demo: https://dl.dropbox.com/u/73678281/SDK-DemoCode/SDK-UsecaseDemo_1.zip) and the ninja (on the test-data library), both with failure.

Oh! Just a thought… you may need to set the buffer like so to get the update to take:

[java]
VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
pvb.setupData(Usage.Stream, 3, Format.Float, pb); // The important bit is Usage
[/java]

@t0neg0d said: @pspeed You are the mesh madman. Any idea why the above would work with some meshes and not others?

Basically it alters the position buffer to normalize model sizes without scaling.

Most likely: animation

Edit: more extensively, try removing all of the skeleton and animation controls and seeing if it works.

2 Likes
@pspeed said: Most likely: animation

Edit: more extensively, try removing all of the skeleton and animation controls and seeing if it works.

Hadn’t even crossed my mind… thanks!

@pspeed said: Most likely: animation

Edit: more extensively, try removing all of the skeleton and animation controls and seeing if it works.

@t0neg0d said: Hadn't even crossed my mind... thanks!

That was a great point xD. Now is working like a charm and just with that:

public void resize(Spatial spatial, final float scale) {
    SceneGraphVisitor visitor = new SceneGraphVisitor() {
        @Override
        public void visit(Spatial spat) {
            if(spat instanceof Geometry) {
                VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
                FloatBuffer positions = (FloatBuffer) pb.getData();

                for(int i = 0; i &lt; positions.capacity(); i++)
                    positions.put(i, positions.get(i) * scale);
            }
        }
    };
    spatial.depthFirstTraversal(visitor);
}

Thanks you very much ;).

1 Like
@NemesisMate said: That was a great point xD. Now is working like a charm and just with that:
    public void resize(Spatial spatial, final float scale) {
        SceneGraphVisitor visitor = new SceneGraphVisitor() {
            @Override
            public void visit(Spatial spat) {
                if(spat instanceof Geometry) {
                    VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
                    FloatBuffer positions = (FloatBuffer) pb.getData();

                    for(int i = 0; i &lt; positions.capacity(); i++)
                        positions.put(i, positions.get(i) * scale);
                }
            }
        };
        spatial.depthFirstTraversal(visitor);
    }

Thanks you very much ;).

Ok… that was a too soon “aleluya” xD. The problem is that I need the controls that I remove but If I add them it again the transformation is “reverted”. Well, this is because of the skeleton control (so the fix comes with resizing bones too):

    public void resize(Spatial spatial, final float scale) {
        SceneGraphVisitor visitor = new SceneGraphVisitor() {
            @Override
            public void visit(Spatial spat) {
                if(spat instanceof Geometry) {
                    VertexBuffer pb = ((Geometry)spat).getMesh().getBuffer(VertexBuffer.Type.Position);
                    FloatBuffer positions = (FloatBuffer) pb.getData();

                    for(int i = 0; i &lt; positions.capacity(); i++)
                        positions.put(i, positions.get(i) * scale);
                }
            }
        };
        
        spatial.depthFirstTraversal(visitor);
        
        SkeletonControl control = spatial.getControl(SkeletonControl.class);
        if(control == null) return;

        for(Bone bone : control.getSkeleton().getRoots()){
            // If don't want to change the whole model (only the instance on the spatial) uncomment
            // the following two lines (bone.setUser..., and comment bone.setBindTransforms(...)
            //bone.setUserControl(true);
            //bone.setUserTransforms(Vector3f.ZERO, Quaternion.IDENTITY, new Vector3f(scale, scale, scale));
            bone.setBindTransforms(Vector3f.ZERO, Quaternion.IDENTITY, new Vector3f(scale, scale, scale));
        }
    }

The problem is that if the bones are changed with setBindTransforms() all instances of the “same model” gets transformed so the the “fix” for that is enabling the UserControl and do a “UserTransform”.

Anyway, in my case is enough with the first way so thanks again for you help.

(I don’t know if this is the right way so if someone has a better way just explain it ;))

Well, the problem I have is that I’m not sure why you go through all of this work to avoid setting a scale at runtime. Seems like a lot of effort for little to no gain.

1 Like