Can jME3 color a Mesh's triangles irrespective of texture?

I was wondering if the jME3 API supports the direct coloring of individual triangles/polygons of a Mesh? I’ve combed through the source on GitHub and don’t think this is possible to do, but wanted to double check here in case anybody can think of a clever solution.

I guess I’m looking for something like:

// WARNING: Pseudo-code
public void colorMeshTrianglePurple(Mesh mesh, int triangleIndex) {
    Vector3f v1, v2, v3;
    v1 = new Vector3f();
    v2 = new Vector3f();
    v3 = new Vector3f();

    mesh.getTriangle(triangleIndex, v1, v2, v3);

    MeshUtils.colorTriangleSurfacePixels(v1, v2, v3, Color.Purple);
}

// EX 1: Without texture
Mesh mesh = getSomehow();
colorMeshTrianglePurple(mesh, 400);

// EX 2: With applied texture
Spatial spatial = asssetManager.loadModel("/path/to/mymodel.obj");
Mesh mesh = MeshUtils.getMeshFromSpatial(spatial);
colorMeshTrianglePurple(mesh, 400);

So in the first example I’m starting with a raw Mesh instance (which I could obtain in a variety of ways), and in the second example I’m using asset manager to load a textured OBJ file. Either way, I’m getting access to a Mesh and trying to color all of the surface pixels of one of its triangles a particular color. All of this “magic” I have encapsulated inside a mysterious utility method called MeshUtils.colorTriangleSurfacePixels.

Is this possible to do, or is the only solution to have multiple textures, each one colored/shaded as desired, and then use jME3 to apply the correct texture on the fly?

You can color the vertexes:
https://jmonkeyengine.github.io/wiki/jme3/advanced/custom_meshes.html

Just make sure the material has use vertex colors enabled… which I think is a different material parameter for lighitng and unshaded. Either way, the tutorials page would be a fine place to start.

Thanks @pspeed!

I followed that tutorial and am hitting an issue when setting the mesh buffer. You can see my full source code here and even reproduce it yourself (if interested) by running ./gradlew run. But the gist of it is that this line:

mesh.setBuffer(Type.Color, 4, colorArray);

…which is taken from this source file here

…causes this exception:

SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.UnsupportedOperationException: The buffer already set is incompatible with the given parameters
    at com.jme3.scene.Mesh.setBuffer(Mesh.java:1052)
    at com.jme3.scene.Mesh.setBuffer(Mesh.java:1072)
    at com.jme3.scene.Mesh.setBuffer(Mesh.java:1076)
    at hotmeatballsoup.jme3troubleshooting.TriangleColorizingVisitor.colorTriangle(TriangleColorizingVisitor.java:61)
    at hotmeatballsoup.jme3troubleshooting.TriangleColorizingVisitor.visit(TriangleColorizingVisitor.java:31)
    at com.jme3.scene.SceneGraphVisitorAdapter.visit(SceneGraphVisitorAdapter.java:61)
    at com.jme3.scene.Geometry.depthFirstTraversal(Geometry.java:462)
    at com.jme3.scene.Node.depthFirstTraversal(Node.java:775)
    at com.jme3.scene.Node.depthFirstTraversal(Node.java:775)
    at com.jme3.scene.Node.depthFirstTraversal(Node.java:775)
    at hotmeatballsoup.jme3troubleshooting.SampleApp.simpleInitApp(SampleApp.java:21)
    at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:220)
    at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
    at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:211)
    at java.lang.Thread.run(Thread.java:745)

Any ideas as to where I’m going awry? Thanks again!

A mesh is a bunch of arrays that all go together. You can’t pick out a single triangle and set a color buffer just for that… you are setting the color buffer for the whole mesh so it must include all of the triangles. Just like the position buffer, normal buffer, etc…

1:1:1:1:1:1

OK, that’s a little disappointing :frowning:

But just to confirm, you’re saying there is no way (at least in jME3) to color a specific handful of triangles inside a mesh?

If that’s true, do you know of any other open source tools (preferably libs that I could drive from the Java layer) that might be able to help me out?

Thanks!

a) that’s not what I said.

b) this is an OpenGL GPU thing.

vertex = pos, color, normal, whatever attribute…1:1:1:1:1:1:1:!

If you want to change the color of just one triangle then grab the whole buffer and change just the colors for that one triangle.

Still trying to understand where the understanding disconnect is. There is no magic here. JME is not complicit.

Edit: put another way: what would you do if you just wanted to change the position of one triangle? It’s the same for every other type of vertex attribute, eg: color.

Thanks @pspeed,

Still trying to understand where the understanding disconnect is. There is no magic here. JME is not complicit.

I think the disconnect is just the fact that you’re a subject matter expert here, and I’m an idiot rookie. Please forgive my ignorance and stupidity as I fumble to make sense of all this!

You can’t pick out a single triangle and set a color buffer just for
that… you are setting the color buffer for the whole mesh so it must
include all of the triangles…If you want to change the color of just one triangle then grab the whole buffer and change just the colors for that one triangle.

OK, so it sounds (again, please forgive my stupidity if I’m still misunderstanding) like you’re saying that my error is that, although I’m plucking out a single triangle from the mesh, that I’m then trying to set the color buffer for the whole mesh.

So to achieve what I’m looking for, I need to:

  1. Grab the whole mesh buffer; and then
  2. Change the colors for just one triangle

So I think I can do something like this:

FloatBuffer wholeBuffer = (FloatBuffer)(mesh.getBuffer(VertexBuffer.Type.Position).getData());
Vector3f v1,v2,v3;
v1 = new Vector3f();
v2 = new Vector3f();
v3 = new Vector3f();

// Populate vectors with triangle #400's data
extractTriangleFromBuffer(400, wholeBuffer, v1, v2 v3);

Where extractTriangleFromBuffer iterates the FloatBuffer and carefully plucks out the correct vertex info for the correct triangle, and populates the 3 Vector3f arguments (I can figure this out later).

So at this point I have accomplished the first part (getting the whole mesh buffer) and I have Triangle #400’s 3 vertices; I believe the last thing I need to do is color this triangle.

I’ve read that the proper way to color a triangle in jME3 is to color the vertices, and jME3 will apply a gradient to the pixels in between them. Not sure if that’s true or false, but I don’t see a setColor method (or anything similar to that) on the Vector3f class.

How can one color a triangle once all 3 vertices (Vector3f instances) are known?

Thanks again for all your help so far!

I mean… what are you trying to do exactly? This?

I did that by using a ray that finds the triangle in the mesh, and using that data, putting a new mesh (which is just three vertices) in the same place (slightly up on the normal vector to avoid z-fighting).

If you want to color any vertex in any color, why not just use vertex colors?

What exactly is it you want to achieve, and maybe there is a better or easier solution.

Do that… but for VertexBuffer.Type.Color.

Imagine you have a bunch of arrays… all the same size. 1:1:1:1:1:1

Each one of those arrays is a vertex attribute. So you have a Position array, a Color array, a Normal array… each of these arrays is the same size. The arrays are the same size because each element of each array corresponds 1:1:1:1:1:1:1:1:1.

So vertex 50 of the position buffer is vertex 50 of the color buffer is vertex 50 of the normal buffer… and so on.

Thanks @jayfella, yes thats exactly what I’m trying to do, simply color a particular triangle pink. Any chance I could see a code snippet that produced the pink triangle in your screenshot?

Yeah sure. I have it set up so I can choose different shapes (single triangle, quad, 2_by_2, etc) so ignore the comparison stuff, but all the code is there for you to go through.

Notice I don’t re-create things in the loop un-necessarily and that I have a timer set up for the ray, because its pointless firing it so many times. And I have set a limit on the ray distance. You may want to change that.

    Ray terrainCollisionRay;
    CollisionResults collisionResults = new CollisionResults();
    float delay = .05f;
    float currentDelay = 0f;    

    // somewhere in your constructor or initializer..
    terrainCollisionRay = new Ray();
    terrainCollisionRay.setLimit(20f);

    @Override
    public void update(float tpf) {

        currentDelay += tpf;

        if (currentDelay >= delay) {
            displayTerrainTriangle();
            currentDelay = 0f;
        }

        if( worldLoc.needsUpdate() ) {
            worldLoc.update();
        }
    }



    private void displayTerrainTriangle() {

        terrainCollisionRay.setOrigin(getApplication().getCamera().getLocation());
        terrainCollisionRay.setDirection(getApplication().getCamera().getDirection());

        terrainNode.collideWith(terrainCollisionRay, collisionResults);

        if (collisionResults.size() < 1) {
            return;
        }

        Triangle triangle = collisionResults.getClosestCollision().getTriangle(null);

        updateSelection(new Vector3f[]{
                collisionResults.getClosestCollision().getGeometry().localToWorld(triangle.get1(), null),
                collisionResults.getClosestCollision().getGeometry().localToWorld(triangle.get2(), null),
                collisionResults.getClosestCollision().getGeometry().localToWorld(triangle.get3(), null)
        });

        collisionResults.clear();
    }


    private void updateSelection(Vector3f[] vertices) {

        // if they are the same, update the mesh,
        // if not, create a new mesh.

        if (this.currentSelectionType == this.lastSelectionType) {

            SelectionUtils.updateMesh(terrainSelectionMesh, vertices);
            terrainSelectionGeom.updateModelBound();
        }
        else {

            this.terrainSelectionMesh = new Mesh();

            SelectionUtils.createMesh(terrainSelectionMesh, vertices, currentSelectionType.getIndexes());

            this.terrainSelectionGeom = new Geometry("terrain-selector-geom", this.terrainSelectionMesh);
            this.terrainSelectionGeom.setMaterial(this.terrainSelectionMat);

            // attach our selector to the world...
            getState(TerrainState.class).getLandNode().attachChild(terrainSelectionGeom);
            terrainSelectionGeom.updateModelBound();

            this.lastSelectionType = this.currentSelectionType;
        }
    }



// a utility class that really shouldnt be static, but suffices for the purpose. don't copy me.

public final class SelectionUtils {

    public static void createMesh(Mesh mesh, Vector3f[] vertices, int[] indexes) {
        mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        mesh.setBuffer(VertexBuffer.Type.Index,    3, BufferUtils.createIntBuffer(indexes));
        mesh.updateBound();
    }

    public static void updateMesh(Mesh mesh, Vector3f[] vertices) {

        FloatBuffer vBuf = (FloatBuffer)mesh.getBuffer(VertexBuffer.Type.Position).getData();

        for (int i = 0; i < vertices.length; i++) {
            BufferUtils.setInBuffer(vertices[i], vBuf, i);
        }

        mesh.setBuffer(VertexBuffer.Type.Position, 3, vBuf);
        mesh.updateBound();
    }

}



oh. and maybe you need this.


public enum SelectionType {

    SINGLE_TRIANGLE(new int[]{ 2,0,1 }),
    QUAD(new int[]{ 2,0,1, 1,3,2 }),
    NONE(null);

    private final int[] indexes;

    SelectionType(int[] indexes) {
        this.indexes = indexes;
    }

    public int[] getIndexes() {
        return this.indexes;
    }
}


Thanks both @pspeed and @jayfella!

So @jayfella thank you for the code snippet, however I’m not seeing where you’re explicitly setting color on a triangle; in fact I don’t see mention of the word “color” (ignoring casing) anywhere in your code. How can that be?!?!

And @pspeed, so it sounds like you’re saying I need to change my first line of code to:

FloatBuffer wholeColorBuffer = (FloatBuffer)(mesh.getBuffer(VertexBuffer.Type.Color).getData());

So now I think I have the entire color buffer for the entire mesh. I need to iterate the data/elements in this color buffer and find, say, the 400th triangle’s colors and then change them. How can I do this? How is the color buffer structured, just RGBA? Meaning if this is a super simple mesh with 3 triangles in it (T1, T2 and T3) will the wholeColorBuffer just consist of:

T1R, T1G, T1B, T1A,
T2R, T2G, T2B, T2A,
T3R, T3G, T3B, T3A

??? If I’m correct about this, then I would just need to iterate to the 2nd set of RGBA values (4 floats) and somehow change them? What Mesh method can I call to set them with new values?

Thanks again to both!

The color is based on the material that you set on the geometry. I just use an unshaded material colored pink.

https://docs.jmonkeyengine.org/beginner/hello_material.html

Thanks @jayfella, if you noticed from my full code example in the GitHub repo, I am already setting an unshaded material. But according to the jME3 docs, I can also color the 3 vertices (somehow) and jME3 will apply a gradient to the pixels (of the triangle for by those 3 vertices).

So I think I’ve figure out how to grab the vertices that make up the triangle I’m interested in, and again I’m using an unshaded material, now I just need to set the vertex color for those 3 vertices. Any ideas?

Iv got this from somewhere. This bit of code selects a pre-selected color from a biome and colors the vertex. It contains everything you need. The code I provided above shows how to modify instead of re-create buffers, which is recommended.


    float[] colors = new float[vertices.length * 4];

    for(int i = 0; i < colors.length; i += 4) {

            Vector3f vert = vertices[i/4];

            Biome biome = world.getBiomeGenerator().getBiome(chunkNode.getLocalTranslation().getX() + vert.getX(), chunkNode.getLocalTranslation().getZ() + vert.getZ());
            ColorRGBA vertColor = biome.getBiomeColors()[colorRandom.nextInt(2)];

            colors[i + 0] = vertColor.r;
            colors[i + 1] = vertColor.g;
            colors[i + 2] = vertColor.b;
            colors[i + 3] = vertColor.a;
        }

        // finally
        geom.getMesh().setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        geom.getMesh().setBuffer(VertexBuffer.Type.Index,    3, BufferUtils.createIntBuffer(indices));
        geom.getMesh().setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
        geom.getMesh().setBuffer(VertexBuffer.Type.Color, 4, BufferUtils.createFloatBuffer(colors));
        geom.getMesh().updateBound();

Thanks @jayfella I’ll give it a try tonight. I assume vertices is the number of vertices in the geom.getMesh()? If not how would I initialize it?

Also how would I instantiate world and chunkNode?

Thanks!

I think this conversation has come far enough for you to venture on your own, your padawan. Go. Be free. I shall light a candle in your honor.

OK I’ll wait a few hours then ask the same thing, only slightly differently, in another question. Thanks for pseudo-help!

Im happy to help man, but i’m not sure you understand what I’ve given you already. Everything you have asked for is right there in plain code for you. I don’t think I can be anymore helpful…