Skip to content

Archive

Tag: Export

This post won’t give you all the information you need if you’re simply new to Blender Scripting. Many of us, however, continued scripting Blender 2.49 because 2.5 scripting wasn’t stable at the beginning. I’m quite sure this post can help you to migrate your scripts from 2.49 although it’s very hands on and I haven’t really tried to organize these useful bits.

  • Export script template (source code). This shows how to arrange metadata, invoke the file selector and write simple output to a file (to make sure something’s actually happening). Includes summary instructions to install the script.

I updated my exporter, Ox3ich, although not with all the features of the current 2.49 version. After trying it out a little I will make it available. In the meantime I list just a few of the many differences between the 2.49 / 2.59 APIs.

Writing I/O scripts for Blender 2.5 isn’t exactly what I’d like to do at the moment. I just have to. Scripting integration looks interesting, with a little more flexibility than in 2.4. Useful links…

As usual, looking at existing scripts will help. The documentation isn’t awesome.

Script Registration

  • The registration process has changed. The easy part is updating your metadata (just look at any of the scripts bundled with 2.5).
  • Scripts now require register/unregister methods. This allows tighter integration as far as I can see, notably it makes it easy/possible for a script to call another script.

Your script/add-on won’t automatically show in the designated menu, but it should appear under [file > user preferences > add-ons] where you can enable it. Then save user prefs (save as default option in the bottom left) to keep the script enabled next time. This page gives a little information about registering add-ons.

2.49 => 2.59 scripting : Miscellaneous changes

  • access current scene - bpy.context.scene
  • get object data (e.g mesh) – object.data
  • Blender.sys.join => os.path.join
  • Writing strings to output stream, see stack overflow answer (python 3.0)
  • mesh.verts => mesh.vertices
  • faces.verts => face.vertices - additionally, face.vertices is now an array of ints representing vertex indexing.
  • face.mat => face.material_index
  • mesh.getFromObject => object.to_mesh(bpy.context.scene,True,”PREVIEW”)
  • vertex.no => vertex.normal (but still uses vertex.co for ‘coord/coordinate’)
  • to change the current frame, use scene.frame_set(). Do not directly assign the frame number. Note that frame numbers start at 0, not 1.

There are changes from Python 2.x to Python 3.x in the way global variables are handled; this combines with a change in the way scripts are invoked, adding up to annoyances when trying a quick and dirty way to output debug info to a file…

Vertex colors

Vertex color data is now separate from face data, stored in Mesh.vertex_colors; additionally this now supports multiple layers, and colors are now in rgb float format (think it used to be rgba, byte format)

  • Iterate layers with something like for layer in mesh.vertex_colors
  • layer.active to find if a layer is active.
  • get layer data: layer.data
  • get vertex color for a face: layer.data[i], will contain corner colors for face at index i.
  • example 1: mesh.vertex_colors[0].data[1].color4[0]
    Red component for corner 4 in face 1, vcol layer 0 of mesh
  • example 2: mesh.vertex_colors[0].data[0].color1.r
  • Red component for corner 1, face 0, vcol layer 0 of mesh

Random links

Exporting cloth animations required only a few changes to the ox3ich exporter.

=> Ox3ich exports blender meshes and animations (source, LGPL).

Since both qwagga (scene export) and ox3ich (mesh/animations export) are now available for download, I guess I’ll want to consolidate this soon with an online project manager. In the meantime… you can have a look at the notes below, outlining how ox3ich handles cloth animations.

Finding Cloth objects

myObject=bpy.data.objects["Dress"] // get the object to be exported

myObject.type == Modifier.Types.CLOTH // confirm this object has ‘cloth’ enabled

We could have an isCloth method, looking like this:

def isCloth(object):

for x in object.modifiers:

if x.type==Modifier.Types.ARMATURE:

return 1

return 0

Using Cloth Objects as equipment

A simple way to generate animations for equipment (see this article) consists in reusing the parent object’s Armature (with extra bones for equipment) and export the actual keyframes (the shape of the mesh as deformed by the armature). Mesh data is memory intensive. One advantage, however, is we don’t need to rewrite / adapt modifiers code.

Now, for equipment, we grab all actions and apply them to the actor’s armature. We’ll do the same for cloth objects. However, there is a difference

In the case of equipment the actor’s armature is on the object’s modifier stack and is really used to animate the object, not the actor.
In the case of cloth, we may not want to animate the cloth with the armature directly. Instead, we apply the armature (and the action) to the actor, which indirectly influences cloth dynamics.

So we need to retrieve the actor used to influence cloth dynamics. I’ll use the same naming convention I used for other equipment items, e.g. dress@player. Then we have:

def getArmatureForCloth(cloth):

influencingActor=bpy.data.objects[cloth.name.split('@')[-1]]

return getArmature(influencingActor)

This uses the naming convention to retrieve the actor. For basic animation export, ox3ich doesn’t need anything else (in code sample ‘object’ is the object using cloth modifier):

exportItemFile(object,path)                      # export basic mesh and material data

actions=getActions(object)                      # return actions for owner (uses naming convention)

armature=getArmatureForCloth(object)            # return the owner’s armature

exportActionFiles(actions,armature,object,path) # export all actions in the usual way.

Restricting key frames for export

By default, the exporter will export mesh data for all action frames, so I’ll extend the actor naming convention to allow us to define a frame range; for example

“player.walk@30-50″ # walk cycle includes only frames 30 to 50 for export

The following function parses the frame range according to this convention

def getFrameRange(action)

range=[1,30000]

parts=action.name.split(‘@’)

if len(parts)==2:

rngString=parts.split(‘-’)

range[0]=int(rngString[0])

range[1]=int(rngString[1])

return range

Notes

This is enough to get started with exporting cloth animations. There are several shortcomings:

  • The exporter does not bake cloth animations, this is done manually (so probably the cloth animation part only works if each file contains only one animation).
  • The exporter doesn’t explicitly handle cyclic animations. With my sample walk cycle, it doesn’t look all that bad, but there has to be a discontinuity between the first and last frame. Minimally the last frame should be replaced by the first frame. For smoother results, two consecutive cycles should be blended together, and I’d like the exporter to do this as well.

In order to get started porting my engine to JME, understanding this example seems like a very good start. It’s also a quick, hands on way to understand M3G and the blender exporter. So I’ll replace the Pogoroo by one of the sample files associated with the blender exporter. This will break the app and I’ll learn how it works by fixing it.

Please refer to my last article for links to this example — won’t be much use if we’re not looking at the code together.

I really liked the Pogoroo example. Apparently this was originally provided by Superscape, and somehow bundle in Sun’s edition of the JME before landing in the Samsung SDK.

I also like the blender M3G exporter. So I did this…

myWorld = (World)Loader.load(“/com/superscape/m3g/wtksamples/pogoroo/content/bebe.m3g”)[0];
//myWorld = (World)Loader.load(“/com/superscape/m3g/wtksamples/pogoroo/content/pogoroo.m3g”)[0];

…after trying this:

myWorld = (World)Loader.load(“c:/bebe.m3g”)[0]; // replace by sample model exported from blender.

Since an absolute path doesn’t work, we can tell that there’s a configuration bit somewhere setting the path for resources.

Now, you’d figure that we can’t replace the original model by another one, and you’re almost right. Except instead of a black screen we get a very cute 3D rendering of a Penguin (Pingoo?) and an engaging exception.

So I think we’re set. We have a working JME project manager, a working simulator and a working assets exporter. Time to understand some of this stuff. Now, this is what triggers an exception:

tRoo = (Group)myWorld.find(POGOROO_MOVE_GROUP_TRANSFORM_ID);
tCams = (Group)myWorld.find(CAMERA_GROUP_TRANSFORM_ID);
acRoo = (Group)myWorld.find(POGOROO_TRANSFORM_ID);
animRoo = (AnimationController)myWorld.find(ROO_BOUNCE_ID);

I haven’t looked at JSR184 in the past 5 years or more, and I never actually used JSR184. The beauty of it is that it doesn’t require an explanation(?).

Here’s what I’m finding:

  • I moved the camera in the original blender file and exported again. The camera updates in the MIDlet, so this camera is actually in use.
  • None of the above calls (tRoo = etc…) does actually trigger an exception, so they’ll just be returning null (I don’t have a debugger setup and line numbers seem wrong, so I’m tracing)

Surely this would trigger a NullPointerException at this point.

AnimationTrack track=null; // changed this way because there is already code to provide defaults instead.
//AnimationTrack track = acRoo.getAnimationTrack(0); // exception!

Once modified as above, we don’t have an exception anymore. We can use the phone simulator’s controls to try to move the Pingoo and it obviously doesn’t budge. More worrying, we don’t get an exception either.

I want to reassign tRoo, tCams, cRoo, animRoo. If we can do that, we’ll know that we know for sure how the exporter generates IDs. But before that I’m really curious to find out why trying to operate the controls doesn’t throw an exception.

So I started tracing this stuff again, and I found that exceptions raised within RefreshTask‘s run() method (Oh my… they use a timer for that!?) aren’t displayed. We can catch them though.

So what do we need?

This is what gets extracted first:

private AnimationController animRoo; // 5, 18
private Group tRoo;
private Group tCams;
private Group acRoo;

In my last article I mentionned that the blender M3G exporter allows the user to specify a user ID for scene nodes – scene nodes are retrieved using an integer – a (hopefully) unique identifier. The Pogoroo example demonstrates the same idea.

I missed several interesting facts about the exporter and the provided example last time:

  1. The exporter assigns ID 0 to anything that doesn’t have a user defined ID (so much for unique IDs?)
  2. In the provided example 2 nodes have ID 1. One is the armature controlling the Pingoo, the other is an animation controller.

The exporter displays export related data (includings IDs) in the blender console. Rudimentary, but it helps.

Bound animRoo

The first item I managed to rebind was animRoo. I used the second animation in the blender file, and this works easily because it has ID 2 by default, and that is a unique ID.

Bound a transform group for the camera (tCams)

In the blender scene browser (I’ll post a screenshot), any object seems to be living under a transform group. However, although tCams expects a transform group, the exporter doesn’t create one for the camera. Instead the transform and the camera seem collapsed to one node.

So I added an ‘Empty’ in blender and parented it from the camera (so the camera is a child of the empty). I named this Empty#25 (25 is an arbitrary int I came up with and voila I managed to bind my camera group.

tRoo and acRoo

I have these two left. Now I have nodes for both the armature and the Pingoo, but they’re not groups. Since I already got into this trap, I thought OK, why should we have two transforms for the Pingoo. Not diving deeper into the code, I’m just guessing there’s an inner transform for the rotation, and an outer transform for the translation. So I made two empties, further complicating the transform hierarchy, but maybe necessary to get this to work. I ‘named’ them 200, 201 and updated the static ints in the java file.

I wouldn’t say this feels like it works as I expected. However…

At the next run, several interesting things happen:

  1. I can see the little Pingoo moving it’s beak. So our animation is working out, big time!
  2. Left/Right keys appear to rotate the Pingoo
  3. Top/Down appear to move the Pingoo

I write ‘appear to’ because these behaviors seem somehow incorrect. I put the camera back in place (facing the pingoo) and moved the transforms (aka tRoo, acRoo) to coincide with the Pingoo’s vertical axis.

Interlude

My cute project manager seems to be getting moody. Also, when I check my process my manager, I find 4 (yes, four!) instances of java running simultaneously. Admittingly they don’t seem very busy, but that’s just a cherry on top of weird taskbar widgets that I didn’t have before installing the JME SDKs (three of these).

Guess I’ll try a ‘cold start’.

Finally

I checked things up after restarting the IDE. The Pingoo is moving along the wrong axis (got bold enough to actually add some terrain so I could get a fair idea). It’s rotating too, but somehow I’m not convinced the camera is following as it should. This seems to be down to axis orientations (and assumptions the ‘game logic’ makes about it).

There’s a lot of good stuff in here. JSR184 clearly supports bone animation, textures and lighting (looks horrible in the simulator) out of the box, never mind a well formed scene-graph. Plus, the exporter gets it right too, although I really need to do something about these IDs.

My engine maybe implements 10% of all this stuff, not to say anything about my exporter. So porting the engine ‘facade’ and reimplementing my game logic on top of that may be much easier than I anticipated. Proof also, than 10% of a game engine can run a pretty nice game. OK… I do have quite a bit of stuff in there that’s clearly not scoped under “3D rendering”  :)

The frame rate is defeating in the simulator, but it doesn’t say much about how it would look on a phone.

I think the sample *.m3g files (and the methods used to load and display them) look neat. I also noticed that Blender has an exporter for *.m3g with support for textures, armatures and animation. Gerhard Völkl clearly put a lot of work in this exporter, topping 3000 lines of code in a single file (a couple dozen classes in there, it’s actually quite readable).

(note: on a PC you can find the source for blender py scripts under Documents and Settings\User\Application Data\Blender Foundation\Blender\.blender\scripts)

The linked page also provides samples files to help us understand how the exporter interprets blender files. There are two things I immediately want to know:

  • How are animations exported? In Blender, bone animations are not directly connected to armatures. Export plugins typically use a kind of naming convention, and this one makes no exception, for example Talk#A1E10#2 is an action, where A1 refers the target armature, E10 means the animation ends at frame 10, and 2 appears to be the node ID (see below).
  • How are node IDs selected? From sample M3G code, it’s fairly obvious that M3G files, for better and for worse, do not contain named entities. Unfortunately the blender M3G exporter uses a naming convention that requires the artist to insert IDs. Maybe this can be improved a little – for example, we could have a java source file with named constants remapping blender object names from arbitrary IDs.

The M3G exporter also allows direct export to java code (in other words, it can generate code which, when run, will produce a scenegraph).

Useful resources

In Blender, Actions contain information about ‘reusable animations’ that can be combined to generate complex animated scenes. Actions can also be used to create catalogues of animations associated with game assets.

I might have suggested before that a 3D artist may prefer to keep several avatar models in the same blender file. Keeping models in separate files makes it (not impossible yet) more difficult to share geometry and materials between models. For the designer, ‘sharing’ may not be a very high level concept, more something like ‘copy, paste and modify’. Nice, fast and simple.

Blender actions are created while posing armatures. Unfortunately, there is not a clear way to retain the connection between an action and the armature first used to generate this action. Here’s a little experiment that illustrates this:

1. Create an armature A with a bone named ‘Bone’. Create an action X for this armature.
2. Create an armature B with a bone named ‘Bone’ (same name). Make action X current in B.
2. Observe the result.

Essentially, Blender Actions are lists of keyframes ‘per channel’; a channel is just a name; the name binds bone names. Any armature can use any action, and this will be ‘effective’ as long as the names match.
That may be powerful. Not great when we want to export per character keyframes for each action.

At this point, we may consider loading a config file listing actions for each actor, or design a little script letting us retain the original armature <> action binding when creating new actions (I think that can be done and integrated nicely with Blender). But I won’t illustrate any of the above this time. I just fallback to a naming convention for my actions, something like:

myActor.jump
myActor.nod

Since all actions are dumped in the same list, this also allows quickly locating per character actions in the Blender interface.

Let’s not learn all about Python Strings

Instead, try this:

z='funbot.jump'
p=z.split('.')
p[0] # prints 'funbot'
p[1] # prints 'jump'

Now we can match each action to a character ‘owning’ this action. You can learn more about actions and *stuff* in previous articles on this blog.

My current export script for character animation requires a naming convention for binding a character to it’s armature.  Also, the script only exports the currently selected mesh.

In this article, I illustrate how to examine a Blender scene to discover assets; I also show how to find the armature modifier associated with an animated character.

In outline, this is how to structure a script to export an asset library:

  • Iterate all objects in the scene
  • Retain only Mesh objects.
  • For each mesh object, check whether this object uses an armature; if so, assume the object is a kind of actor or character.
  • Export mesh objects as appropriate (You can learn how to do this from previous articles on this blog)

Interactive session

Let’s try the following:

s=Blender.Scene.GetCurrent()
s.objects
for x in s.objects:
	x.type

(After typing ‘x.type’ in the interactive console, press enter twice to effect the loop)

This will print the type of each object in the order they are stored in the scene. We only want to export Mesh objects. Pick any Mesh object using an armature. (in my case, index 1 in the object list).

object=s.objects[1]
for x in object.modifiers:
	if(x.type==Modifier.Types.ARMATURE):
		x

This will print something like:

[Modifier "Armature", Type "Armature"]

That’s all we need to identify actors and props in a Blender Scene.

Material assignments allow defining per face or per object materials. In this interactive scripting session I show how materials, material properties and material assignments can be accessed. Next time I will expose a simple solution for exporting character animations.

Per face material assignments

For the record, material assignments are accessible from [buttons window > editing tab > Link and Materials panel. There's the usual up/down arrow to select an existing material or creating a new one. Although the actual material editor is elsewhere, you can still choose a color and a name for a material created here without exiting the editing tab. Upon creating / selecting a material, the current selection is assigned this material. Or you can select vertices later and use the 'Assign' button.

Interactive Scripting Session

This may not require additional comments:

bot=Blender.Object.Get("FunBot")
mesh=bot.getData(mesh=1)
faces=mesh.faces

# this will print the material index
faces[0].mat

# to find the name of a material
# get materials as an array
materials=mesh.materials
# this prints the name of the material assigned to face zero.
materials[faces[0].mat]

# this illustrates material properties
# (in interactive mode, prints material property values)
mat=materials[0]
mat.rgbCol
mat.amb
mat.alpha
math.hard/512
mat.specCol
mat.emit

Material.Get() can be used to get all materials in a scene - for an export script, however, it may be more efficient to map materials as we export geometry to avoid exporting unused materials.

Finally, there's an *awful* lot more about Blender materials in the Material API (link to Blender docs).

In Blender, an Action is a sequence of key frames representing a character gesture (walking, jumping, slashing, falling, etc…). Actions are used as building blocks in Blender’s non linear animation (NLA) editor. We can use actions to create ‘animation catalogues’. An export script using actions might be used to batch export all animations for one or several characters, so we can update our game assets whenever we need, which simplifies and enhances flow.

Interactive Scripting Session

For this session, I have created a little robot – ‘funbot’ and an armature for it.
I then create two actions and detach them from the funbot. To take advantage of actions, we need to make each action current, i.e. for every action we attach the action to the armature and export all frames or key frames (either the armature frames/keyframes or the deformed mesh) before moving to the next action. Time for scripting:

#retrieve the action dictionary
actions=Blender.Armature.NLA.GetActions()

#get an action named 'nod' (as defined by your animator)
action=actions.get("nod")

#the following prints '[Action "nod"]'
armature=Object.Get("FunBotArmature")

#this makes the action current for the armature,
#so we can playback the action and record it.
action.setActive(armature)

#even if you call this, the window won't update, but the underlying
#model does, which is what we need for export.
scene=Blender.Scene.GetCurrent()
scene.update(1)

The above script does nearly everything we need related to actions – giving access to all defined actions within a blender file, it retrieves an action by name, applies it to the correct armature and updates the scene; at this point we’re nearly ready to iterate action frames and export the model and/or armature to a file (see my interactive session on exporting meshes deformed using an armature)

Just one more thing we need is knowing the number of frames in the animation. We can actually get better than just the number of frames – instead, we can get the list of keyframes, in ascending order:

#get the list of keyframes
keys=action.getFrameNumbers()
#print all keyframe indices, like ' [1,11,24,36,] '
list(keys)
#print the length of the animation
keys[len(keys)-1]

Using this, we can choose to either export only keyframes (use less memory), or all frames in the animation (OK for really simple animations and models).

Before I forget… for some reason the Blender APIDoc doesn’t just turn up whenever I google it. Here it is.

Just a while back I published a weeny export script and matching C/Objective C code that can be used to export mesh data from blender to OpenGL vertex arrays.

To export animated characters, we need to do either of two things:

  1. Export Armature key frames or frames, along with their attachment to target objects; then apply the deformation either in real time, or maybe just before we need to animate the model. The upside is that this is memory savvy. The downside is that if your game engine can’t handle your exported armatures (I don’t know how likely to happen this might be) or you don’t use a game engine, you’ll have to work out deformations yourself, starting from a fairly complex high level model.
  2. Export ready mesh key-frames or frames, with the armature effect already applied. The downside is that this requires a lot more memory.

My hunch is that how much ‘a lot more’ matters really depends how large your models are and what kind of platform you’re running on. I plan running on the iPhone/iTouch with isometric views. This means that my 3D sprites will be small – hence require less polygons. For some animations, a little compression (e.g. retaining unchanged coordinates from a previous frame) can be considered if needed, never mind sharing geometry across models.

In this article I show how to force modifiers to be applied before exporting mesh data – one way to attach an armature in blender is via modifiers. This is a good opportunity to try interactive scripting with Blender, making learning scripting easy and fun :)

Effecting the modifier stack also means that you can export complex operations applied to your object, not just armature modifiers.

Give back to Caesar what belongs to  Chicken – I borrowed this technique from the Blender to Panda3D export script – Chicken exports to Panda3D’s *.egg format,

***Believe it or not, *.egg came first, then Chicken somehow assisted the birth of this tutorial.

An Interactive Scripting Session

First I make a triangle pointing up in the z x plane, ensuring x==0 for the middle vertex. I add an armature – just one bone – aligned on the z axis and pose it to drag the top vertex to the right, about 1 unit. Finally I add the armature as modifier. I think this is important, afaik this wouldn’t work if the armature parented
the mesh. Then I type the following in the scripting console:

scene=Blender.Scene.getCurrent()
obj =scene.objects[1]
mesh=obj.getData(mesh=1)

# next line prints coord without deformation
mesh.verts[2].co.x

# this is where the magic happens
# getFromObject implicitly applies all modifiers.
tmesh.getFromObject(obj,0,1)

#next line prints coord with deformation
tmesh.verts[2].co.x

OK, this shows that we can export the deformed mesh. Now we need to get each
frame. So I insert a keyframe for the rotation of the bone (pose mode),
go 10 frames further and pose with the tip of the bone pointing right.

First, let’s repeat the first experiment,
browsing to frame 11 (left/right arrow keys)

tmesh.getFromObject(obj,0,1)
tmesh.verts[2].co.x

This shows that the script is, by default,
querying the current frame.
I found a command to change the frame:

context = scene.getRenderingContext()
# set the current frame
context.currentFrame(3)
scene.update(1)
tmesh.getFromObject(obj,0,1)
tmesh.verts[2].co.x

If you repeat this operation using the frame value, you will see that the vertex coordinate moves at every frame.

Next time I’ll look into exporting several actions for a given character.