Convert my Blender Assets to j3o with Gradle

Hi there
I made a big step forward and automated my very simple pipeline. I post here the code as it might inspire someone… or not. Most of the stuff I found somewhere in the jmonkey engine code and just adapted it to my need.

This is the converter which in my case loads blender files and converts them to j3o files. GLTF is no my roadmap, but for my blender 2.79 models this is good enough. I’m sure it is easy to extend to GLTF support.

package ch.artificials.bubble;

import com.jme3.asset.DesktopAssetManager;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.scene.Spatial;
import javassist.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;

public class AssetConverter {
    static Logger LOG = LoggerFactory.getLogger(AssetConverter.class);
    public static final String DESKTOP_ASSET_CONFIG = "/com/jme3/asset/Desktop.cfg";

    private Path root;
    private DesktopAssetManager assets;

    public AssetConverter(File assetRoot) {
        try {
            this.root = assetRoot.getCanonicalFile().toPath();
        } catch (java.io.IOException e) {
            throw new RuntimeException(String.format("Error getting canonical path for '%s'", assetRoot), e);
        }
        LOG.info("Using source asset root '" + root + "'");

        URL assetConfig = getClass().getResource(DESKTOP_ASSET_CONFIG);
        LOG.info("Found assetConfig '{}'", assetConfig);

        this.assets = new DesktopAssetManager(assetConfig);
        assets.registerLocator(root.toString(), FileLocator.class);
    }

    public Spatial loadModel(File f) throws NotFoundException, IOException {
        if (!f.exists()) {
            throw new NotFoundException(String.format("Model %s does not exist", f));
        }

        try {
            f = f.getCanonicalFile();
        } catch (IOException e) {
            throw new IOException(String.format("Something went wrong with '%s'", f), e);
        }
        String path = root.relativize(f.getAbsoluteFile().toPath()).toString();

        LOG.info("Loading asset '{}'", path);

        assets.clearCache();

        return assets.loadModel(path);
    }

    public void saveModel(Spatial spatial, String model) throws IOException {
        LOG.info("Save asset '{}'", model);
        BinaryExporter.getInstance().save(spatial, new File("assets/Models/" + model));
    }
}

Then the main class

package ch.artificials.bubble;

import com.jme3.scene.Spatial;
import javassist.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Stream;

public class AssetConvert {
    static Logger LOG = LoggerFactory.getLogger(AssetConvert.class);

    public static void main(String[] args) throws IOException {
        AssetConverter assetConverter = new AssetConverter(new File("/path/to/my/blender/Models"));

        Stream.of(new File("/path/to/my/blender/Models").listFiles())
                    .filter(file -> !file.isDirectory())
                    .filter(file -> file.getName().endsWith(".blend"))
                    .forEach(file -> {
                        String model = file.getName().substring(0, file.getName().lastIndexOf('.')) + ".j3o";
                        try {
                            LOG.info("Convert {} --> {}", file.getCanonicalPath(), model);
                            Spatial spatial = assetConverter.loadModel(file);
                            assetConverter.saveModel(spatial, model);
                        } catch (IOException | NotFoundException e) {
                            LOG.error("Could not convert to model '{}'", model);
                        }
                    });

    }

}

and last but not least the gradle integration


task convertAssets(type: JavaExec) {
    group = "Execution"
    description = "Run the main class with JavaExecTask"
    classpath = sourceSets.main.runtimeClasspath
    main = 'ch.artificials.bubble.AssetConvert'
}

And with gw convertAssets all my blender files get converted nicely to j3o files :slight_smile:

3 Likes

And I got rid of the old netbeans IDE and use now intelliJ which I also use at work. Pretty neat.

1 Like

Is there a reason that GitHub - Simsilica/JmeConvert: A command line utility for converting models to J3O and copying their dependencies to a new target structure. did not work for you?

1 Like

like Paul said.

in your case, it will build ALL models (for bigger games it might take minutes) while there are ready-solutions that convert only changed models.

Anyway glad you share it, for small games its nice.

1 Like

I’ll give that a try.

Well yeah that’s true I do not check if it has been changed or something. Just pure convert, takes 2 or 3 seconds in my case.

1 Like

i see you are saving new .j3o models, so this should only take time while !dir.contains("file-name"+".j3o"), i mean only during the first run isn’t it ?

Nah, I also want to update my models, so just look if it is already there isn’t enough. I would probably need to store the time stamps of the blend files or something in this direction. Anyway I just wanted a cheap solution for me as doing it with the jmonkey netbeans manually was not fun at all. And I wanted to have a pure intelliJ solution.
I guess the right solution is @pspeed s tool…
Anyway I saw a lot of those model loader problems here and I thought ok, this might be the salvation for one or the other. Turns out it’s not. But was for me :smiley:

1 Like

JmeConvert also lets you run scripts while processing the models. You can tag materials for export as j3m, child models as export for asset link nodes, and so on. Lots under the hood there.

1 Like