JmeConvert auto-loader... w/question

As many of you know, I developed the JMEC (JmeConvert) tool some time back. It works pretty well to convert models to j3o and can even run post-processing scripts on them to make adjustments during the conversion process.

Yesterday, I threw together a prototype of an autoloader. Essentially, you can add this to your scene and it will automatically reconvert the source on the fly if the file changes. It even detects when scripts change.

Here is a video demonstrating what I mean:

This is all I had to do to add that model to my test scene:

        JmecNode jmecNode = new JmecNode(new File("samples/test-model.gltf"));
        jmecNode.addModelScript(new File("sampleScripts/test-script.groovy"));
        rootNode.attachChild(jmecNode);

This is the relevant part of the script I edited in the video:

// Move the model so that it's base is at y=0
def bound = model.modelRoot.getWorldBound()
model.modelRoot.setLocalTranslation(0, (float)(bound.yExtent - bound.center.y), 0);

Itā€™s some pretty standard script code I use to make a model stand on the ground.

Question:
I wonder how folks might envision using this?

For myself, I was thinking Iā€™d add this to my game temporarily while I worked on the model and then switch to regular asset loading. So the JmecNode in the example code above was the very simplest way to do that.

But it occurs to me that maybe some folks would want to be able to easily switch back and forth between auto-convert mode and ā€œproductionā€ mode where it just loads the converted asset.

This could either be done with some kind of JmecLoader that would provide auto-convert nodes or regular AssetLinkNodes depending on the mode it was inā€¦ or I might be able to work this as an actual AssetLoader where adding the loader or not adding the loader changes what would happen for some special type of asset key.

That last bit might be tricky so I thought Iā€™d see what folks might want before diving into the asset loader swamp.

Thoughts?

8 Likes

Thatā€™s pretty nice. The best way to make something useful is if its useful also to the one who creates it. So if you got any more ideas where your asset workflow could be improved than I am happy to listen.

I wrote a convert tool as well and while they got their similarities, mine was written for the following purposes.

Basically, imagine a blender file that is filled with a geometry for a level. In one layer, a geometry for level, some geometry prefixed with ā€˜collisionā€™ for collision geometry, ā€˜eventā€™ for a collision geometry that triggers an action, lines geometry as paths for npc to follow, polygon to represent the location of water etc.
In another layer, there are some common objects, egs doors, chairs, etc, and they are grouped into Blender groups. And these groups are used as group instances in the level.

Now, to get this blender converted to jme:

  • each Blender group got to go to a separate .j3o file
  • group instances got to use AssetLinkNode
  • etc, eg: use j3o material with same name as material in Blender

Now, you can imagine if one has etc 10 groups and 1 level that would be 11 .j3o files. Not to mention the need to use AssetLinkNodes. How would one accomplish this task without an asset import tool? Not to mention how long it would take and for every change to the file one would have to go through the process againā€¦

To do the above I wrote a tool, which accepts an import script that tells which file to convert and into what. Writing an import script is trivial and then the import process is done with a click of a button and can be repeated if the level is edited.

Now, how does the import script look like, what does it contain? To give an example here is a little snippet.

global {
    blendpath: [ "/project data/Env" ]
    assetpath: "/jme project name/assets"
    out-path: /Scenes
    out-texture-path: /Textures
    copy-texture: true
}
valley {
    in-blend: mistvalley/mistvalley.blend 
	material: [
		["ColMat", "Materials/PR/COL/Col.j3m"]
		["ColWallMat", "Materials/PR/COL/ColWall.j3m"]
		["ColFloorMat", "Materials/PR/COL/ColFloor.j3m"]
	]
	group: [
		["Pillar1", "Models/MV/Pillar1.j3o"]
		["Pillar2", "Models/MV/Pillar2.j3o"]
		["Pillar3", "Models/MV/Pillar3.j3o"]
		["Pillar4", "Models/MV/Pillar4.j3o"]
		["Pillar5", "Models/MV/Pillar5.j3o"]
		["RockStairs", "Models/MV/RockStairs.j3o"]
	]
    out-j3o: [ 
				["Terrain", "mvrawheight.j3o"]
				["Rocks", "valley.j3o"]
				["mvfinal", "mvfinal.j3o"]
				["mvmap", "mvmap.j3o"]
             ]
	usegroups: true
	keepnodes: true
}

Now regarding reloading assets while game is running, in my case, I reload the class that creates levels. And the class is responsible for creating the whole level again. So i do not hot-swap assets but levels.

While hot-swapping assets as your post described is nice, these are some limitations that I think your current solution might be under:

  • hot-swapping asset may lead to exception eg when code retrieves child by name/or gets controlā€¦ and there is none
  • what if several assets depend on each otherā€¦

An option to reload by pressing a button instead of automatically would be nice to address the above limitation.

Btw, I also wonder, regarding your workflow. I described my workflow above, What about yours? Do you generally use 1 blend file for 1 j3o, or create many j3oā€™s from single blender as mentioned above?

Personally, I make models in Blender, not scenes. I find Blender to be very clunky for scene creation and anyway, many folks will have their own style.

Most of my games use generated levels, thoughā€¦ or terrain, etcā€¦ Not at all conducive to Blender editing.

Do you use the actual .blend format or gltf?

I would think it would be pretty easy to get JMEC to split files up based on user data or something.

I see, for me Blender is nice for creating scenes but I can see there can be trouble with using terrain. But I do use it with terrain as well. I create a mesh that represents terrain and then an import script ray casts the mesh to create a heightmap. An alternative would be to use subdivided quad with heightmap + displace modifier and paint heigh map directly in blender.

I got some generated parts of a level as well and yes for those I just create models in Blender.

To answer your question, currently, it uses .blend file and com.jme3.scene.plugins.blender to read it and convert it. I wrote it like almost 3 years ago, so at that time I didnā€™t know of gltf or a Blender plugin that would output gltf.

This is very cool @pspeed!

I can see using this for some form of a modding framework. Something that can be used to swap out models/other assets at runtime when loading the mods after the game has been loaded.

I will have to play with this, the engine I built has similar features, but works completely different.

I will try to get something committed today even if itā€™s not ā€œofficialā€ yet.

1 Like

It could be that Iā€™m too much of a noobā€¦ but painstakingly making sure stuff is exactly on the ground or on walls, etcā€¦ is a pain. Where thatā€™s trivial with a in-game editor.

So how do you determine what parts of your scene should be separate instances? Is that a custom exporter in blender or something you look at in the scene? Or are these some kind of instancing in blender and the blender loader just knows that?

For this, you can use Blenderā€™s Magnet icon, snap to Increment/Face.

To handle groups, groups instances so that AssetLinkNodes are created:

The blender file to be converted is first preprocessed with a python script into a tmp.blend file. The python script loads the desired objects to be converted, if the object is group instance and assetlinknode should be used it replaces it with an empty object and adds an user property to identify the group.
Then this tmp.blend file is converted and user properties are checked and assetlinknodes created. Finally the result is saved into j3o.

PS:
The blend file is prepocessed from java by executing blender through java process API and by using Blenderā€™s --background --python args.

String[] args = new String[] {
    "blender-2.76b-linux-glibc211-x86_64/blender",
    "--background",
    "--python",
    "assetscript2.py",
    "--",
    dir,
    name,
    section,
    item,
    blendFile.getAbsolutePath(),
	useGroups?"usegroups":"nogroups",
	removeMods?"removemods":"applymods"
};
Runtime.getRuntime().exec(args);

This is the blender version?

Yes, the one from 2015 I think, that I use for what I do atm.

Imgur

When I was creating the vehicle showcase thing I was constantly toying with the models. It would have been a major help with that.

It would also be nice to have some kind of event fired for when the model gets reloaded so we can re-create a collision shape, etc.

A nice one-liner or something to switch between ā€œdev modeā€ and production mode would be icing on the cake.

Yeah, thinking of something similar for error cases, tooā€¦ like when you screw up a script it can do more than simply removing the model from the scene (with some default behavior of showing a big error icon or something.)

Does anyone have an example blender scene I can use where the objects are ā€œinstancesā€ that would ideally be shared in the JME scene? I was going to see what the gltf has in it in these cases but donā€™t trust myself to ā€œdo it rightā€ in Blender.

Hmm, doesnā€™t gltf use a single mesh already if a blender contains duplicates/group instances? I am not sure, youā€™d have to check, but in this case this might work by default.

I donā€™t know because Iā€™m not sure Iā€™m doing it right.

I tried ā€œDuplicate Linkedā€ which seems to treat them as essentially instances of the same object in blender but they are multiple objects in gltf. Iā€™m not sure gltf has the concept of instancing.

Yes they should be multiple objects because they got different local transform, but the mesh should be same.

The easiest is to import a simple scene that uses duplicated cube and output the mesh objects, eg check if they are the same obj in jme after import.

Iā€™m trying to make that scene because none has been provided.

Yes, the meshes are shared in my example.

Iā€™m looking for something in between, I guess. Blender tends to encourage an object to be one mesh but itā€™s often the case that you make one ā€œobjectā€ of multiple meshes. For example, a chair and its seat cushion might be separate objects. I can parent->child these but Iā€™ve not (easily) found a way to treat them essentially like one object without joining them. Joining feels destructive to me for my purposes.

I do think GLTF would support this but Iā€™m having trouble making a blender example that shows it.

Also, maybe someone knows this, tooā€¦

Surely Blender must support something similar where external files can be linked into a scene and if the original file is changed then reloading the scene will have new versions. I canā€™t imagine the feature animations are made without thisā€¦ ie: Iā€™m sure every table, chair, etc. is not embedded forever right into that scene.

nice work :slight_smile: very good tool, when often update model/scene

btw. im not sure why in video you made render instead of just click third icon here:
image

edit:

for a character edit i cant use this because there is background process, but if your api provide option to make some special ā€œjava process of loaded scene on changeā€ then it might work even here.