[Solved] Cannot change buffers in a mesh

Hello!

I’m messing around with custom meshes and it seems that I cannot modify a newly created sphere mesh. What I’m trying to do just for the purpose of testing is to modify the normal buffer and set every value to zero. However, when I create a new geometry based on the modified mesh, the normals act as if they were not changed, i.e. the sphere is perfectly lit.

This is the main entry that creates a simple sphere with a phong material.

public class FlatShadingMain extends SimpleApplication {

private Geometry sphereGeo;

public static void main(String[] args) {
    FlatShadingMain app = new FlatShadingMain();
    app.start();
}

@Override
public void simpleInitApp() {
    flyCam.setMoveSpeed(10f);
    
    Sphere sphereMesh = new Sphere(32, 32, 2f);

    FlatShadedNormalCalculator.computeNormals(sphereMesh);

    sphereGeo = new Geometry("Shiny rock", sphereMesh);
    sphereMesh.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
    // TangentBinormalGenerator.generate(sphereMesh); // for lighting effect
    Material sphereMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    sphereMat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
    sphereMat.setBoolean("UseMaterialColors", true);
    sphereMat.setColor("Diffuse", ColorRGBA.White);
    sphereMat.setColor("Specular", ColorRGBA.White);
    sphereMat.setFloat("Shininess", 64f); // [0,128]
    sphereGeo.setMaterial(sphereMat);
    rootNode.attachChild(sphereGeo);

    /** Must add a light to make the lit object visible! */
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
}

@Override
public void simpleUpdate(float tpf) {
    sphereGeo.rotate(0, FastMath.PI * tpf, 0);
}
}

And this is the code that is supposed to set every normal to zero or clear the normal buffer, depending on what is commented and what is not.

public static <T extends Mesh> T computeNormals(T mesh) {
  /*  FloatBuffer normals = mesh.getFloatBuffer(Type.Normal); // Not working
    for (int i = 0; i < normals.position(); ++i)
        normals.put(i, 0f);
    mesh.setBuffer(Type.Normal, 3, normals); */
    
    VertexBuffer normalBuffer = mesh.getBuffer(Type.Normal);
    for (int elementIndex = 0; elementIndex < normalBuffer.getNumElements(); ++elementIndex)
        for (int componentIndex = 0; componentIndex < normalBuffer.getNumComponents(); ++componentIndex)
            normalBuffer.setElementComponent(elementIndex, componentIndex, 0f);
    
    // mesh.clearBuffer(Type.Normal); Not working too
    
    normalBuffer.setUpdateNeeded(); // Is it needed?
    mesh.updateBound();
    
    return mesh;
}

Am I doing something wrong or am I missing a method that I need to call?

Thank you! :slight_smile:

If it helps, you can look at how I update buffers in the DMesh class:

It’s the approach to updating buffers that is the most efficient and maybe it shows something you missed.

I tried to adapt your code for my situation to see if it would magically work and it still doesn’t :confused: Now I don’t know if it’s supposed to work or there something that I missed. It is really frustrating, as I followed the tutorial and when I look at the code, it’s supposed to work. However, the buffer just doesn’t want to update and no signs of malfunction are given, i.e. exception or documentation.

On another note, why would you rewind buffers and jungle like that just to update them? Is a deep clone necessary to update a vertex buffer?

Thanks for the help :smiley:

public final class MeshBufferUtil {

public static void updateMeshBuffer(Mesh sourceMesh, Type vertexBufferType) {
    VertexBuffer sourceVertexBuffer = sourceMesh.getBuffer(vertexBufferType);
    VertexBuffer targetVertexBuffer = matchBuffer(sourceMesh, sourceVertexBuffer);

    morph(sourceVertexBuffer, targetVertexBuffer);
    sourceMesh.updateBound();
}

private static void morph(VertexBuffer sourceVertexBuffer, VertexBuffer targetVertexBuffer) {
    FloatBuffer sourceFloatBuffer = (FloatBuffer) sourceVertexBuffer.getData();
    sourceFloatBuffer.rewind();

    FloatBuffer targetFloatBuffer = (FloatBuffer) targetVertexBuffer.getData();
    targetFloatBuffer.rewind();

    morph(sourceVertexBuffer, sourceFloatBuffer, targetFloatBuffer);

    sourceFloatBuffer.rewind();
    targetFloatBuffer.rewind();
    targetVertexBuffer.updateData(targetFloatBuffer);
}

private static void morph(VertexBuffer referenceVertexBuffer, FloatBuffer sourceFloatBuffer, FloatBuffer targetFloatBuffer) {
    for (int elementIndex = 0; elementIndex < referenceVertexBuffer.getNumElements(); ++elementIndex)
        for (int componentIndex = 0; componentIndex < referenceVertexBuffer.getNumComponents(); ++componentIndex)
            if (sourceFloatBuffer.hasRemaining())
                targetFloatBuffer.put(sourceFloatBuffer.get());
}

private static VertexBuffer matchBuffer(Mesh sourceMesh, VertexBuffer sourceBuffer) {
    VertexBuffer target = sourceMesh.getBuffer(sourceBuffer.getBufferType());
    if (target == null || target.getData().capacity() < sourceBuffer.getData().limit()) {
        target = sourceBuffer.clone();
        sourceMesh.setBuffer(target);
    } else
        target.getData().limit(sourceBuffer.getData().limit());

    return target;
}

private MeshBufferUtil() {
}
}

I don’t do any deep cloning. I just access the buffers sequentially which is the most efficient way of accessing buffers… thus they need to be rewound because they will often be at the end when I grab them.

They need to be rewound before handing them back to JME just in case it doesn’t do it… since it will also access them sequentially.

Indexed access to ByteBuffers can be slower than streamed access.

Ok I see. I didn’t quite understand the purpose of your code, but now I get it :slight_smile: I tried to do this instead just to see if I could mess with the shape’s position buffer, but in vain.

Random rand = new Random();                                                                
VertexBuffer positionVertexBuffer = mesh.getBuffer(Type.Position);                         
FloatBuffer positionFloatBuffer = (FloatBuffer) positionVertexBuffer.getData();            
positionFloatBuffer.rewind();                                                              
                                                                                           
for (int i = 0; i < positionFloatBuffer.limit(); ++i)                                      
    positionFloatBuffer.put(rand.nextFloat() * 2);                                         
                                                                                           
positionVertexBuffer.updateData(positionFloatBuffer); // Resend the owned buffer to the GPU
mesh.updateBound();                                                                  

Could there be a reason of why I can’t change the position buffer of a simple sphere shape?

Nope… either you aren’t really changing it or you aren’t really changing the mesh you think you are. But the code you posted already seems simple enough.

Try it with Box or one of the other meshes to make sure Sphere() isn’t doing something strange.

1 Like

It worked with a box! :smiley: Even though it’s still weird that it didn’t work with a sphere. I’ll investigate a little bit to see if it’s due to my incompetence or a bug.

Thanks a lot!

Probably this line in Sphere:

Ohhhhh I found out what the problem was! I tried again with a simple sphere and it worked, so I then tried to do the exact same thing as before :

sphereGeo = new Geometry("Shiny rock", mesh);                                  
mesh.setTextureMode(Sphere.TextureMode.Projected); // HERE IS THE PROBLEMATIC LINE
TangentBinormalGenerator.generate(mesh);            

I found out that setting the texture mode breaks the shape buffer update, even though the buffer update is done before the geometry creation.

Sphere mesh = new Sphere(32, 32, 2f);                                          
                                                                               
FlatShadedNormalCalculator.computeNormals(mesh);                               
                                                                               
sphereGeo = new Geometry("Shiny rock", mesh);                                  
mesh.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
TangentBinormalGenerator.generate(mesh); // for lighting effect

Ah, yeah, I think it recalculates all of the mesh.

1 Like

I think it’s the setGeometryData() called from the setTextureMode(…) method because it changes the normals and positions. Anyway, thanks again for your help!