I’m trying to make an artificial horizon for my space ship console. It is a two color sphere with a brown lower hemisphere and a blue upper hemisphere. (I’d like keep colors changeable at runtime so that when you are in space, the bottom would be white and the top black.)
I could 1) make a two color sphere in Blender, but I don’t know if I can change the colors, 2) make a material that takes two colors, (I did the exercises and can make a MatDef), 3) make a ColorMap with two colors. (Would it only need to be 2 pixels in height? Does it get scaled?) 4) I was looking at Shaders…
I like option 2) I’m trying to do it with a material. I can make a MaterialDef that just changes the color of the whole sphere. It seems reasonable to assume that, in the .vert file, I could say:
if (inPosition.y >- 0)
m_Color = topColor
else
m_Color = bottomColor
I haven’t seen any access to the position components nor have I seen any control statements (‘cept #ifdef).
How can I write a MatDef to do that or how else can I do it.
/*
* a very simple fragment shader for use with Bicolor.j3md
*/
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform vec4 m_TopColor;
uniform vec4 m_BottomColor;
varying vec2 texCoord1; // from vertex shader
void main() {
// Set frag color based on 2nd (V) component of texCoord1.
// In a com.jme3.scene.shape.Sphere with TextureMode.Original,
// V increases linearly (from 0 to 1) with the local Z coordinate,
// so the "horizon" is at V=0.5 .
if (texCoord1.y >= 0.5) {
gl_FragColor = m_TopColor;
} else {
gl_FragColor = m_BottomColor;
}
}
Bicolor.vert:
/*
* a very simple vertex shader which always generates texCoord1
* (used by Bicolor.j3md)
*/
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/Skinning.glsllib"
#import "Common/ShaderLib/Instancing.glsllib"
attribute vec3 inPosition;
attribute vec2 inTexCoord;
varying vec2 texCoord1; // to fragment shader
void main(){
texCoord1 = inTexCoord;
vec4 modelSpacePos = vec4(inPosition, 1.0);
gl_Position = TransformWorldViewProjection(modelSpacePos);
}
Bicolor.j3md:
// material definition for a simple, unshaded material with exactly 2 colors
MaterialDef Bicolor {
MaterialParameters {
Color BottomColor (BottomColor)
Color TopColor (TopColor)
}
Technique {
VertexShader GLSL100: Shaders/Bicolor.vert
FragmentShader GLSL100: Shaders/Bicolor.frag
WorldParameters {
WorldViewProjectionMatrix
ViewProjectionMatrix
ViewMatrix
}
}
}
Main.java
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Sphere;
/*
* Demo for Bicolor material.
*/
public class Main extends SimpleApplication {
double elapsedTime = 0f; // seconds
boolean inSpace = false;
Material bicolor;
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void simpleInitApp() {
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
cam.setLocation(new Vector3f(3f, 1.4f, -3.2f));
cam.setRotation(new Quaternion(0.15701026f, -0.39636666f, 0.06899879f, 0.90193146f));
flyCam.setMoveSpeed(5f);
Mesh ballMesh = new Sphere(40, 40, 1f);
Geometry ballGeometry = new Geometry("Attitude Indicator Ball", ballMesh);
rootNode.attachChild(ballGeometry);
Quaternion upright = new Quaternion();
upright.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Z.negate(), Vector3f.UNIT_Y);
ballGeometry.setLocalRotation(upright);
bicolor = new Material(assetManager, "MatDefs/Bicolor.j3md");
ballGeometry.setMaterial(bicolor);
updateMaterials();
}
@Override
public void simpleUpdate(float tpf) {
/*
* toggle the environment every 2 seconds
*/
elapsedTime += tpf;
if (elapsedTime > 2.0) {
toggleEnvironment();
elapsedTime = 0.0;
}
}
void toggleEnvironment() {
inSpace = !inSpace;
updateMaterials();
}
void updateMaterials() {
if (inSpace) {
bicolor.setColor("BottomColor", ColorRGBA.White.clone());
bicolor.setColor("TopColor", ColorRGBA.Black.clone());
} else {
bicolor.setColor("BottomColor", ColorRGBA.Brown.clone());
bicolor.setColor("TopColor", ColorRGBA.Blue.clone());
}
}
}
I’m getting the dreaded
java.lang.ArrayIndexOutOfBoundsException: 1
at com.jme3.material.plugins.J3MLoader.readParam(J3MLoader.java:237)
at com.jme3.material.plugins.J3MLoader.readMaterialParams(J3MLoader.java:290)
at com.jme3.material.plugins.J3MLoader.loadFromRoot(J3MLoader.java:541)
at com.jme3.material.plugins.J3MLoader.load(J3MLoader.java:555)
at com.jme3.asset.DesktopAssetManager.loadAsset(DesktopAssetManager.java:288)
at com.jme3.material.Material.(Material.java:120)
at my.asteroid.states.HUDAppState.getTestSphere(HUDAppState.java:156)
at my.asteroid.states.HUDAppState.initialize(HUDAppState.java:92)
at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:251)
at com.jme3.app.state.AppStateManager.update(AppStateManager.java:281)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:245)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:744)
I took the space out from main() and the open bracket, per an old post.
I need a break. sick with a cold and am hungry…
And, thanks.
I did the upgrade. It works, sort of. I get the bottom color on top and bottom. (So, soled color) But the flashing to the two modes works. (Good to know)
I almost lost my whole project. I hacked into InputManager to add modifiers (shift/control) and to do it, I needed to copy a bunch of stuff and put it in JME-core.dir and us it as my main library. So when I upgraded, it was no longer valid. I had to recreate my whole project folder and copy the src and Assets over.
So, you see two colors… I’m trying different values for the 0.5 but, I don’t know why its not working. Oh, and, Dah, I should have tried real Java code in the Main(). That said, OpenGL and java are not the same.
Btw you may have noticed my fragment shader used texture coordinates (passed from the vertex shader using a varying) instead of position data, as you originally envisioned. This approach assumes a textured model. I’m sure it’s also possible to bypass texturing entirely and simply pass position data or color data to the fragment shader.