Hi Sploreg!
First off, thanks for giving the most important information: it should work. So there must something I’m doing wrong…
I wanted to post snippets of my code, but it’s split up across many wrapper classes so I decided to make a test project based on the TerrainGridTest project, with very few changes except for the material. Run the project to demonstrate the problem. Change the field “defaultShininess” from values 1.0f to 20.0f to make both tests.
Here is the code:
[java]
package jme3test.terrain;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.ScreenshotAppState;
import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.shader.VarType;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.grid.ImageTileLoader;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.Namer;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ShininessChangeTest extends SimpleApplication {
private float defaultShininess = 1.0f; // or 20.0f for no fade test
private Material mat_terrain;
private TerrainGrid terrain;
private float grassScale = 64;
private float dirtScale = 16;
private float rockScale = 128;
private boolean usePhysics = false;
public static void main(final String[] args) {
ShininessChangeTest app = new ShininessChangeTest();
app.start();
}
private CharacterControl player3;
@Override
public void simpleInitApp() {
File file = new File(“TerrainGridTestData.zip”);
if (!file.exists()) {
assetManager.registerLocator(“http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip”, HttpZipLocator.class);
} else {
assetManager.registerLocator(“TerrainGridTestData.zip”, ZipLocator.class);
}
this.flyCam.setMoveSpeed(100f);
ScreenshotAppState state = new ScreenshotAppState();
this.stateManager.attach(state);
mat_terrain = new Material(assetManager, “Common/MatDefs/Terrain/TerrainLighting.j3md”);
mat_terrain.setBoolean(“useTriPlanarMapping”, false);
mat_terrain.setFloat(“Shininess”, defaultShininess);
//The line below is to eliminate black spots on the terrain
mat_terrain.setBoolean(“WardIso”, true);
//default config… use unknown
mat_terrain.setBoolean(“isTerrainGrid”, true);
//
//material.setBoolean(“UseMaterialColors”,true);
mat_terrain.setColor(“Diffuse”, ColorRGBA.White);
mat_terrain.setColor(“Specular”, ColorRGBA.White);
//Create Splat Textures
// GRASS texture
Texture grass = assetManager.loadTexture(“Textures/Terrain/splat/grass.jpg”);
grass.setWrap(WrapMode.Repeat);
mat_terrain.setTexture(“DiffuseMap”, grass);
mat_terrain.setFloat(“DiffuseMap_0_scale”, grassScale);
// DIRT texture
Texture dirt = assetManager.loadTexture(“Textures/Terrain/splat/dirt.jpg”);
dirt.setWrap(WrapMode.Repeat);
mat_terrain.setTexture(“DiffuseMap_1”, dirt);
mat_terrain.setFloat(“DiffuseMap_1_scale”, dirtScale);
// ROCK texture
Texture rock = assetManager.loadTexture(“Textures/Terrain/Rock2/rock.jpg”);
rock.setWrap(WrapMode.Repeat);
mat_terrain.setTexture(“DiffuseMap_2”, rock);
mat_terrain.setFloat(“DiffuseMap_2_scale”, rockScale);
this.terrain = new TerrainGrid(“terrain”, 65, 257, new ImageTileLoader(assetManager, new Namer() {
public String getName(int x, int y) {
return “Scenes/TerrainMountains/terrain_” + x + “_” + y + “.png”;
}
}));
this.terrain.setMaterial(mat_terrain);
this.terrain.setLocalTranslation(0, 0, 0);
this.terrain.setLocalScale(1f, 1f, 1f);
this.rootNode.attachChild(this.terrain);
TerrainGridLodControl control = new TerrainGridLodControl((Terrain)this.terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control);
final BulletAppState bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
this.getCamera().setLocation(new Vector3f(0, 200, 0));
this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
DirectionalLight light = new DirectionalLight();
light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
rootNode.addLight(light);
if (usePhysics) {
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
player3 = new CharacterControl(capsuleShape, 0.5f);
player3.setJumpSpeed(20);
player3.setFallSpeed(10);
player3.setGravity(10);
player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
bulletAppState.getPhysicsSpace().add(player3);
terrain.addListener(new TerrainGridListener() {
public void gridMoved(Vector3f newCenter) {
}
public Material tileLoaded(Material material, Vector3f cell) {
return material;
}
public void tileAttached(Vector3f cell, TerrainQuad quad) {
while(quad.getControl(RigidBodyControl.class)!=null){
quad.removeControl(RigidBodyControl.class);
}
quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
bulletAppState.getPhysicsSpace().add(quad);
}
public void tileDetached(Vector3f cell, TerrainQuad quad) {
bulletAppState.getPhysicsSpace().remove(quad);
quad.removeControl(RigidBodyControl.class);
}
});
}
this.initKeys();
}
private void initKeys() {
// You can map one or several inputs to one named action
this.inputManager.addMapping(“Lefts”, new KeyTrigger(KeyInput.KEY_A));
this.inputManager.addMapping(“Rights”, new KeyTrigger(KeyInput.KEY_D));
this.inputManager.addMapping(“Ups”, new KeyTrigger(KeyInput.KEY_W));
this.inputManager.addMapping(“Downs”, new KeyTrigger(KeyInput.KEY_S));
this.inputManager.addMapping(“Jumps”, new KeyTrigger(KeyInput.KEY_SPACE));
this.inputManager.addListener(this.actionListener, “Lefts”);
this.inputManager.addListener(this.actionListener, “Rights”);
this.inputManager.addListener(this.actionListener, “Ups”);
this.inputManager.addListener(this.actionListener, “Downs”);
this.inputManager.addListener(this.actionListener, “Jumps”);
}
private boolean left;
private boolean right;
private boolean up;
private boolean down;
private final ActionListener actionListener = new ActionListener() {
@Override
public void onAction(final String name, final boolean keyPressed, final float tpf) {
if (name.equals(“Lefts”)) {
if (keyPressed) {
ShininessChangeTest.this.left = true;
} else {
ShininessChangeTest.this.left = false;
}
} else if (name.equals(“Rights”)) {
if (keyPressed) {
ShininessChangeTest.this.right = true;
} else {
ShininessChangeTest.this.right = false;
}
} else if (name.equals(“Ups”)) {
if (keyPressed) {
ShininessChangeTest.this.up = true;
} else {
ShininessChangeTest.this.up = false;
}
} else if (name.equals(“Downs”)) {
if (keyPressed) {
ShininessChangeTest.this.down = true;
} else {
ShininessChangeTest.this.down = false;
}
} else if (name.equals(“Jumps”)) {
ShininessChangeTest.this.player3.jump();
}
}
};
private final Vector3f walkDirection = new Vector3f();
@Override
public void simpleUpdate(final float tpf) {
Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
this.walkDirection.set(0, 0, 0);
if (this.left) {
this.walkDirection.addLocal(camLeft);
}
if (this.right) {
this.walkDirection.addLocal(camLeft.negate());
}
if (this.up) {
this.walkDirection.addLocal(camDir);
}
if (this.down) {
this.walkDirection.addLocal(camDir.negate());
}
if (usePhysics) {
this.player3.setWalkDirection(this.walkDirection);
this.cam.setLocation(this.player3.getPhysicsLocation());
}
Float mat_Shininess = ((Float)mat_terrain.getParam(“Shininess”).getValue());
if(mat_Shininess < 20f) {
mat_Shininess = mat_Shininess + 0.01f;
mat_terrain.setParam(“Shininess”, VarType.Float, mat_Shininess);
}
Logger.getAnonymousLogger().log(Level.SEVERE, "Shininess: " + mat_Shininess.toString());
}
}
[/java]
Well well well… I found out something interesting with this test that I didn’t see in my project. Here the tiles are much smaller than in my project. It seems the new shininess is applied to the new tiles as they are loaded but it is not applied to the tiles that are already loaded… Is it because each tile gets a copy of the material as they are loaded rather than a reference? Huh!!! That a look a that, it seems I found out the problem. If I add these lines of code under the line “mat_terrain.setParam(“Shininess”, VarType.Float, mat_Shininess);” in the test project, it works just fine:
[java]
for(Spatial spatial: terrain.getChildren()) {
if(spatial instanceof TerrainQuad) {
TerrainQuad terrainQuad = (TerrainQuad)spatial;
terrainQuad.getMaterial().setParam(“Shininess”, VarType.Float, mat_Shininess);
}
}
[/java]
Alright, problem solved!
Thanks Sploreg for putting me on the right track. 