[SOLVED] Multicolored Cube - There must be an easier way

Having a cube with each side having a different color seems like something everyone needs. There is a great page that says that they can be used for retro/1990s games.

I needed one for my Rubik’s Cube and I did it in Blender by unwrapping a cube and getting U,V coordinates. It’s fine. Still, it seems like a lot of work for something so useful that I think a lot of people could use. The link below shows how to do it in opengl.
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-4-a-colored-cube/#draw-a-cube

  1. Can I make a cube/box with a list of vertices and a list of colors?
    Or…
  2. I have two shaders.
    2.A) With the first one, input six colors to use for the sides. If I could get the index of the vertex, I could use it to set the color. Two per side. How can I get a vertex index?

2.B) I input a 3 by 4 pixel BufferedImage ColorMap that I generated. I need 1) to add U,V coordinates and 2) to stop the blending between colors. I found a JME UVCoordinatesGenerator but, I need to download the plugin but can’t find it online.
http://javadoc.jmonkeyengine.org/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.html
//import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator

  1. Or…
    Is there another way to make a multicolored cube?
1 Like

I believe option (1) is the way to go. Create a custom mesh based on com.jme3.scene.shape.Box and add a vertex buffer of type Color. Then apply a material with setBoolean(“UseVertexColor”, true) .

3 Likes

https://jmonkeyengine.github.io/wiki/jme3/advanced/custom_meshes.html
There’s a section for custom colors

2 Likes

Hummmm…
Would you use “Common/MatDefs/Misc/Unshaded.j3md” or a custom shader? Unshaded.j3md has everything in it; VertexColor for HAS_VERTEXCOLOR.
I set things up like this, but get all black unless I setColor(ColorRGBA.Blue);

Edit: Final code using “Common/MatDefs/Misc/Unshaded.j3md”

private Box box = null;
private Geometry getSixColorCube(){
    Geometry geometry = null;
    Material material = null;
    float[] colorArray;//fixed this

    box = new Box(1f, 1f, 1f);
    geometry = new Geometry("SixColorBox", box);
    material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    colorArray = getColorArray(box);//fixed this

    box.setBuffer(VertexBuffer.Type.Color, 4, colorArray);
    material.setBoolean("VertexColor", true);
    geometry.setMaterial(material);
    
    upright.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Z.negate(), Vector3f.UNIT_Y);//1, -1, 1
    geometry.setLocalRotation(upright);
    //geometry.setLocalTranslation(10f, 7f, -15f);
    return(geometry);
}


private float[] getColorArray(Mesh mesh){
    float[] tempColorArray = new float[mesh.getVertexCount() * 4];
    float r = 1.0f, g = 0.0f, b = 0.0f, a = 1.0f;

    for (int y = 0;y < mesh.getVertexCount()/2;y++){
        if (y == 0 || y == 1){//bottom
            r = 0.0f;g = 1.0f;b = 0.0f;a = 1.0f;//green
        }
        else if (y == 2 || y == 3){
            r = 1.0f;g = 1.0f;b = 1.0f;a = 1.0f;//white
        }
        else if (y == 4 || y == 5){
            r = 0.0f;g = 0.0f;b = 1.0f;a = 1.0f;//blue
        }
        else if (y == 6 || y == 7){
            r = 1.0f;g = 1.0f;b = 0.0f;a = 1.0f;//yellow
        }
        else if (y == 8 || y == 9){
            r = 1.0f;g = 0.0f;b = 0.0f;a = 1.0f;//red
        }
        else if (y == 10 || y == 11){
            r = 1.0f;g = 0.5f;b = 0.0f;a = 1.0f;//orange
        }
        else{
            r = (float)Math.random();g = (float)Math.random();b = (float)Math.random();a = 1.0f;
        }
        println("tempColorArray[" + y + "]=" + r + "," + g + "," + b + "," + a);
        tempColorArray[y * 4 * 2 + 0] = r;//four floats * 2 verts + index
        tempColorArray[y * 4 * 2 + 1] = g;
        tempColorArray[y * 4 * 2 + 2] = b;
        tempColorArray[y * 4 * 2 + 3] = a;

        tempColorArray[y * 4 * 2 + 4] = r;
        tempColorArray[y * 4 * 2 + 5] = g;
        tempColorArray[y * 4 * 2 + 6] = b;
        tempColorArray[y * 4 * 2 + 7] = a;

    }

    return(tempColorArray);
}

I think the color buffer should contain colors (groups of 4 floats, each between 0 and 1), not integers.

Hello,

i’m currently busy with understanding shaders and one of my challenges was to create that six-color-cube i liked so much in Java3D. I was really suprised how easy it could be to solve tasks not on mesh but on shader base, so here is what i did to easily create such a six-color-cube (btw thanks for the Shader-Wiki at https://jmonkeyengine.github.io/wiki/jme3/advanced/jme3_shaders.html which was my starting point):

  1. Create a material definition file “ColorBoxShader.j3md”:
MaterialDef ColorBoxShader Material
{
  MaterialParameters
  {
  }
  Technique
  {
    VertexShader GLSL100: MatDefs/ColorBoxShader.vert
    FragmentShader GLSL100: MatDefs/ColorBoxShader.frag

    WorldParameters
    {
      WorldViewProjectionMatrix
    }
  }
  Technique FixedFunc
  {
  }
}

and store it in /assets/MatDefs.

  1. Create a vertex shader file “ColorBoxShader.vert” which defines the colors depending on each side’s normal:
uniform mat4 g_WorldViewProjectionMatrix;

attribute vec3 inPosition;
attribute vec3 inNormal;

varying vec4 varColor;

void main()
{
  gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

  float red =
      inNormal.y > 0.0 || inNormal.x < 0.0 || inNormal.z < 0.0 ? 1.0 : 0.0;
  float green =
      inNormal.y < 0.0 || inNormal.x < 0.0 || inNormal.z > 0.0 ? 1.0 : 0.0;
  float blue = inNormal.y != 0.0 || inNormal.x > 0.0 ? 1.0 : 0.0;

  varColor = vec4(red, green, blue, 1.0);
}

and store it in /assets/MatDefs.

  1. Create a fragment shader file “ColorBoxShader.frag” which simply sets the color passed from “ColorBoxShader.vert” to “ColorBoxShader.frag” by the variable “varColor”:
varying vec4 varColor;

void main()
{
  gl_FragColor = varColor;
}

and store it in /assets/MatDefs.

  1. Now you can use the material definition file “assets/MatDefs/ColorBoxShader.j3md” with the box mesh provided by jme3:
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

public class HelloColorBoxShader extends SimpleApplication
{
  Geometry geometry;
  
  public static void main(String[] args)
  {
    HelloColorBoxShader app = new HelloColorBoxShader();
    app.start(); // start the game
  }
  
  @Override
  public void simpleInitApp()
  {
    Box mesh = new Box(1f, 1f, 1f);
    geometry = new Geometry("ColorBox", mesh);
    Material material = new Material(assetManager,
        "MatDefs/ColorBoxShader.j3md");
    geometry.setMaterial(material);
    rootNode.attachChild(geometry);
  }
  
  @Override
  public void simpleUpdate(float tpf)
  {
    super.simpleUpdate(tpf);
    
    geometry.rotate(tpf, tpf, tpf);
  }
}

Regards,
ekipur

2 Likes

Or, as sgold suggests you can do it the easy way:

mesh.setBuffer(Type.Color, 4, new float[] {
        1, 0, 0, 1,
        1, 0, 0, 1,
        1, 0, 0, 1,
        1, 0, 0, 1,
        0, 1, 0, 1,
        0, 1, 0, 1,
        0, 1, 0, 1,
        0, 1, 0, 1,
 ...and so on, four per side 
    });
...
material.setBoolean("UseVertexColor", true);
1 Like

I tried that and wanted to pass in the 6 colors. I could pass them in, but I didn’t know how to tell the vertex which color to use. Using Unshaded.j3md works really well.

Hello Mipada,

in my shader example the six colors magenta, green, blue, red, yellow and cyan are calculated in the vertex shader “ColorBoxShader.vert” just to simulate that six-color-cube known from Java3D.

To vary the colors and to be able to pass six colors of your choice to the shader you have to define six color attributes (similar to the “Color” attribute in Unshaded.j3md). This is done by

  1. Replacing the MaterialParameters section of the “ColorBoxShader.j3md” with:
  MaterialParameters
  {
    Vector4 TopColor
    Vector4 FrontColor
    Vector4 RightColor
    Vector4 BackColor
    Vector4 LeftColor
    Vector4 BottomColor
  }
  1. Making these six color attributes known to the vertex shader “ColorBoxShader.vert” so they can be used depending on the cube’s face normals (replace the whole file “ColorBoxShader.vert” with the following code):
uniform mat4 g_WorldViewProjectionMatrix;

uniform vec4 m_TopColor;
uniform vec4 m_FrontColor;
uniform vec4 m_RightColor;
uniform vec4 m_BackColor;
uniform vec4 m_LeftColor;
uniform vec4 m_BottomColor;

attribute vec3 inPosition;
attribute vec3 inNormal;

varying vec4 varColor;

void main()
{
  gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

  if (inNormal.y > 0.0) {
    varColor = m_TopColor;
  } else if (inNormal.y < 0.0) {
    varColor = m_BottomColor;
  } else if (inNormal.x > 0.0) {
    varColor = m_RightColor;
  } else if (inNormal.x < 0.0) {
    varColor = m_LeftColor;
  } else if (inNormal.z > 0.0) {
    varColor = m_FrontColor;
  } else if (inNormal.z < 0.0) {
    varColor = m_BackColor;
  } else {
    varColor = vec4(1.0, 1.0, 1.0, 1.0);
  }
}
  1. Finally, pass the colors of your choice to the material (you need only to replace the simpleInitApp() method):
  @Override
  public void simpleInitApp()
  {
    Box mesh = new Box(1f, 1f, 1f);
    geometry = new Geometry("ColorBox", mesh);
    Material material = new Material(assetManager,
        "MatDefs/ColorBoxShader.j3md");
    material.setColor("TopColor", ColorRGBA.Magenta);
    material.setColor("FrontColor", ColorRGBA.Green);
    material.setColor("RightColor", ColorRGBA.Blue);
    material.setColor("BackColor", ColorRGBA.Red);
    material.setColor("LeftColor", ColorRGBA.Yellow);
    material.setColor("BottomColor", ColorRGBA.Cyan);
    geometry.setMaterial(material);
    rootNode.attachChild(geometry);
  }

Regards,
Ekipur

1 Like

Thanks all. All three methods work well. It is nice being able to change the colors on the fly. Oh and thanks for the inNormal line. I still can’t read that doc.

(Now to tackle the U,V one…)

2 Likes

Note: the shader approach is the more expensive approach both in your coding time and in performance… but it is a good learning exercise if you ultimately want to write shaders.

The color buffer approach is the fastest overall and you can still set the colors on the fly if you want.

Edit: (You can also set different colors per corner, etc. for gradients, change UVs in the same way, and so on… pretty flexible for a lot of things short of writing a custom shader and a good lead on to a custom shader.)

2 Likes