I usually use lemur’s built in color chooser because I always thought it caused issues trying to get base java gui components like jfx or awt into jme, but it looks like you got it to work so that looks good to me!
That would also be useful. I wanted to do some animation slowing/pausing to demonstrate the effect better, but I avoided putting any animation code into the class I wrote for managing each blend-layer, since a game would typically handle the animations elsewhere, and I want to make sure that I write this BlendLayerEffect.java class to also be usable outside of a test-case too.
It would also be useful if there were a way to make a new matParam editor window that only displays (or omits) a list of designated params that match the indicated names. That way the container won’t overflow and go off the screen (although a scroll container would also fix that).
For example, each blend layer could have its own small matParam container that only shows the params that have a name starting with “BlendLayer_i_” where i is the index of that layer.
Here’s the code for the BlendLayerEffect.class so far, to show how I set the matParams with a method in this class so it sets the param on the whole list of materials that the effect is applied to (rather than editing one material at a time).
public class BlendLayerEffect {
private int layerIndex = -1;
private String layerParamsPrefix;
private final Vector4f blendVec = new Vector4f();
public float getBlendVar(){ return blendVec.x; }
private final ArrayList<Material> materials = new ArrayList<>();
public BlendLayerEffect(int layerIndex, ArrayList<Material> materials) {
setLayerIndex(layerIndex);
this.materials.addAll(materials);
}
public BlendLayerEffect(int layerIndex, Spatial spatial) {
setLayerIndex(layerIndex);
addMaterialsFromSpatial(spatial);
}
public void setBlendValue(float blendVal){ blendVec.setX(blendVal); }
public void clearLayer(){
for(Material mat : materials){
ArrayList<MatParam> matParams = new ArrayList<>(mat.getParams());
for(MatParam matParam : matParams){
if(matParam.getName().startsWith(layerParamsPrefix)){
mat.clearParam(matParam.getName());
}
}
}
}
public void setLayerIndex(int layerIndex){
if(layerIndex != -1){
clearLayer();
}
this.layerIndex = layerIndex;
layerParamsPrefix = "BlendLayer_" + layerIndex;
for(Material mat : materials){
mat.setVector4(layerParamsPrefix + "_BlendVec", blendVec);
}
}
public void registerMaterial(Material material){
String blendVecMatParamString = "BlendLayer_" + layerIndex +"_BlendVec";
if(material != null && material.getMaterialDef().getMaterialParam(blendVecMatParamString) != null){ //detect if the material's matDef is valid and has support for the blend layer
if(!materials.contains(material)){
materials.add(material);
if(materials.size() > 1){//if this isn't the first material added, copy all params from the first material to the newly registered one
//to-do
}
material.setVector4(blendVecMatParamString, blendVec);
}
}
}
public void addMaterialsFromSpatial(Spatial spatial){
spatial.breadthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
registerMaterial(geom.getMaterial());
}
});
}
private boolean isTriplanar = true;
public void setTriplanar(boolean isTriplanar) {
this.isTriplanar = isTriplanar;
if(baseColorMap != null){
baseColorMap.setWrap(Texture.WrapMode.Repeat);
}
if(normalMap != null){
normalMap.setWrap(Texture.WrapMode.Repeat);
}
if(metallicRoughnessAoMap != null){
metallicRoughnessAoMap.setWrap(Texture.WrapMode.Repeat);
}
if(emissiveMap != null){
emissiveMap.setWrap(Texture.WrapMode.Repeat);
}
}
public void setParam(String name, VarType varType, Object val){
for(Material mat : materials){
if(val == null){
mat.clearParam(name);
}else{
if(val instanceof Texture && isTriplanar){
((Texture) val).setWrap(Texture.WrapMode.Repeat);
}
mat.setParam(name, varType, val);
}
}
}
public void setBaseColorMap(Texture texture){
baseColorMap = texture;
setParam(layerParamsPrefix + "_BaseColorMap", VarType.Texture2D, texture);
}
public void setNormalMap(Texture texture){
normalMap = texture;
setParam(layerParamsPrefix + "_NormalMap", VarType.Texture2D, texture);
}
public void setMetallicRoughnessAoMap(Texture texture){
metallicRoughnessAoMap = texture;
setParam(layerParamsPrefix + "_MetallicRoughnessAoMap", VarType.Texture2D, texture);
}
public void setEmissiveMap(Texture texture){
emissiveMap = texture;
setParam(layerParamsPrefix + "_EmissiveMap", VarType.Texture2D, texture);
}
}
The way I wrote this is best for managing the blend-layers dynamically in a game, but it currently makes it difficult for this class to work with your matParam editor, since it sets the matParam directly to one material at a time.
(Having a scroll container to make the main matParam container fit all the params would be nice too, but I still don’t think lemur has an official scroll container implementation unfortunately. And the scroll container I made isn’t very good so I don’t know if it would be worth copy/pasting all the code it relies on into this project)
Here’s also a snippet of code from a sloppy and unfinished MaterialEditor container I use in my own custom edtior. This is the code I use to omit or include certain matParams from the display:
public void setMaterialToEdit(Material materialToEdit){
paramsContainer.clearChildren();
this.materialToEdit = materialToEdit;
if(materialToEdit != null){
materialNameLabel.setText(materialToEdit.getName());
matDefNameLabel.setText(materialToEdit.getMaterialDef().getName());
ArrayList<MatParam> shaderParams = new ArrayList(materialToEdit.getMaterialDef().getMaterialParams()); //
for(int s = 0; s < shaderParams.size(); s++){
MatParam shaderParam = shaderParams.get(s);
MatParam matParam = materialToEdit.getParam(shaderParam.getName()); //check for existing value in materialToEdit
boolean isExcluded = false;
String paramName = shaderParam.getName();
for(String startingWithString : excludeParamsStartingWithList){
if(paramName.startsWith(startingWithString)){
isExcluded = true;
}
}
if(!isExcluded){
this.initMaterialParam(matParam, shaderParam);
}
}
}
}
This way the matParam editor automatically shows every param unless it is omitted, rather than having to code which ones to display. So I usually add “ShadowMap” to the omission list for example, that way the material editor isn’t spammed with redundant shadowMap_0-5 declarations that a user should never be setting manually anyways.
And in the case of this shader demo, it could be useful to omit all of my BlendEffect_ params from the master-materialParam editor container, and instead put these blendLayer params each in their own small editing container that syncs the params to the BlendLayerEffect class rather than directly to the material