Statically Typed Materials

After trying to use the material parameters for unshaded.j3md when I meant to use the material parameters for lighting.j3md for the umpteenth time I decided to see if I could do anything about it and make materials statically typed

Replacing this:

Material shipMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
shipMaterial.setTexture("DiffuseMap", someTexture);
shipMaterial.setTexture("GlowMap", someOtherTexture);
shipMaterial.setBoolean("UseVertexColor", true);
shipMaterial.setColor("Diffuse", ColorRGBA.White);
shipMaterial.setColor("Ambient", ColorRGBA.White);

With this

LightingMaterial shipMaterial = new LightingMaterial(assetManager);
shipMaterial.setDiffuseMap(someTexture); <--- these setters take typed arguments, not "Object"
shipMaterial.setGlowMap(someOtherTexture);
shipMaterial.setUseVertexColor(true);
shipMaterial.setDiffuse(ColorRGBA.White);
shipMaterial.setAmbient(ColorRGBA.White);

So I made a gradle plugin TypedMaterials to do just that. It scans the .j3md files (both locally and in dependencies) and synthesises classes based on the parameters in the MaterialParameters section of the definition. All classes are created in src/main/generated/java which is added to source.Sets.main.java.srcs in gradle. I haven’t hand typed any of these classes so they should stay up to date with new JME versions.

These materials extend the Material class so can be used anywhere a Material could be.

And (where available on the material) include the documentation as a javadoc

image

How do I use the plugin

Bare minimum

In the simplest case where you only want JME materials you can add the following to your build.gradle

buildscript{
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "com.onemillionworlds:typed-materials:1.0.0"
    }
}
plugins {
    // java plugins as normal
}

apply plugin: 'com.onemillionworlds.typed-materials'

typedMaterials {
    jmeMaterials()
}

//jme dependencies as normal

This will provide statically typed materials for the materials found in jme3-core and jme3-effects

Local materials

The plugin can also provide statically typed material classes for your own local materials. By default it assumes these materials are found within your module at src/main/resources/MatDefs; if that’s the case this is the config

typedMaterials {
    jmeMaterials()
    localMaterialsSearch('com.yoursite.package.to.create.materials.in')
}

If you have your materials in a non standard location that can be passed as additional parameters

typedMaterials {
    jmeMaterials()
    localMaterialsSearch('com.yoursite.package', 'path/to/assets/materials', 'folderAtAssetsRoot')
}

e.g. (if migrating from the old assets folder)

typedMaterials {
    jmeMaterials()
    localMaterialsSearch('com.yoursite.package', 'assets', 'assets')
}

The first parameter is the path to where to find the materials (where to search) and the second is the folder that is the root of the assets (which is used to figure out how much of the directories structure to snip off for the asset name).

If you are also using lombok you may need to use a separate module; see Lombok-compatibility

Non JME library materials

If a 3rd party library is providing materials these can also be registered to generate java classes

typedMaterials {
    ....
    librarySearch("libraryNameMaterials", ".*libraryname.*", "com.libraryname.materials")
}

The parameters are: task name, regex to select library, package to create materials in

Wrappers

In addition to the typed Materials (e.g. UnshadedMaterial) there are also wrappers (e.g. UnshadedMaterialWrapper) these are useful if you are not constructing a material but obtaining one in another way (e.g. using the getter on a material)

LightingMaterialWrapper lightingMaterial = new LightingMaterialWrapper(someGeometry.getMaterial()));
lightingMaterial.setDiffuseMap(someDiffuseMap)

These do not extend Material but you can get the underlying material back again if needed

lightingMaterial.getMaterial();

Material Factories

In addition to the Typed Materials and wrappers a MaterialFactory will also be generated. It can be used like this

MaterialFactory factory = MaterialFactory.INSTANCE;
LightingMaterial = factory.createMaterial(LightingMaterial.class);

This can be useful for projects that want to use mocking in their tests as the MaterialFactory can be mocked (e.g. using Mockito) to produce mock materials (which would otherwise need a live AssetManager).

4 Likes

Can you tell that I’m a java true believer; “this small part that isn’t statically typed, could it perhaps be statically typed”

3 Likes