How to convert Blender file to j30 binary *outside* of the SDK/IDE?

I recently watched this excellent 4-part video series on rigging and animating 3D models and then importing them into jME3. In the third video the author opens up the jME SDK/IDE (NetBeans) and converts his Kang.blender file into a Kang.j3o file using a right-click menu.

I’m wondering if its possible to do the exact same conversion outside of the SDK/IDE? Perhaps there is a command-line tool I could use?

See JMEUtils/AssetsImport at master · rainmantsr/JMEUtils · GitHub

Thanks @tsr - any docs on how to run it?

No, sorry. But its only one class, so it should be quite easy to reuse… To load the .blender and write a .j3o is actually only 4 lines of code - the rest is about also exporting the textures, material definitions, generating tangents and preserving some directory structure that i need in my project…

Edit: I start it with gradle run -PappArgs="-asset_dir,…/Locomotion/assets,-import_dir,…/Assets/,-create_tangents"
this “appArgs” is just some “hack” to get commandline arguments through gradle to my app. If you build it and run the binary from commandline you can pass the arguments “normal”. asset_dir is the target directory for the .j3o and stuff and the import_dir is where the .blender is.

https://wiki.jmonkeyengine.org/doku.php/jme3:advanced:save_and_load

@tsr - awesome, thanks again!

@normen - are you suggesting I build my own Savable? I can’t quite infer what you mean by just posting the link… So something like a BlenderSavable that loads a .blender file and saves it in .j3o format?

No he just posted you the link to the wiki where is the example of how to “save a node”. So you just load the .blender file using the assetmanager and then run the code from “save a node” and you have a .j3o…

Thanks again @tsr

So is this as simple as something like:

Node myBlenderModel = (Node)assetManager.loadModel('models/myBlenderModel.blender');
BinaryExporter exporter = BinaryExporter.getInstance();
File myJMEModel = new File("path/to/models/myJMEModel.j3o");
try {
    exporter.save(myBlenderModel, myJMEModel);
} catch (IOException ex) {
  ....
}

Is this it?!? Or are there any caveats/pitfalls? Above you mentioned something about materials not being converted correctly, or something :slight_smile:

Yes thats basically it. And I didn’t say that the materials are not converted correctly. But this way you will not get external material files (material properties are also stored in your resulting .J3O) and there is nothing done with the textures - so the path are the same as in blender, so if the have been stored relative to the .blender files in blender they need to be also in the same relative directory where you later load the .J3O…

This is what we do in our editor.

At the moment we only really use it to convert .obj files but it should handle whatever you want and have a loader for. Use as you wish if you wish.

Also note that it registers a FileLocator path for the parent folder of the input file and then at the end it unregisters it.

import com.jme3.asset.AssetManager;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.scene.Spatial;
import com.jme3.util.TangentBinormalGenerator;

import java.io.File;
import java.io.IOException;

public class ModelConverter {

    private final AssetManager assetManager;

    public ModelConverter(final AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    public void convert(final File inputFile, final File outputFile, final boolean generateTangents) {
        final String parentPath = inputFile.getParent();
        assetManager.registerLocator(parentPath, FileLocator.class);

        try {
            final Spatial modelToImport = assetManager.loadModel(inputFile.getName());

            if (modelToImport != null) {
                if (generateTangents) {
                    TangentBinormalGenerator.generate(modelToImport);
                }

                final BinaryExporter binaryExporter = new BinaryExporter();

                if (outputFile.getParentFile().exists() || outputFile.getParentFile().mkdirs()) {
                    try {
                        binaryExporter.save(modelToImport, outputFile);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } finally {
            assetManager.unregisterLocator(parentPath, FileLocator.class);
        }
    }
}
1 Like

Awesome, @DannyJo I’ll give this a try (as well as @tsr’s Gradle script) tonight when I get home. Thank you!

1 Like