Chicken, an Egg exporter for Blender 2.49 and lower

Hmmm, didn’t realise that. Though seeing as flatten strong allows you to move from the first to the second I’m not sure what the advantage of allowing Chicken to export the second is. Could be maybe added, but there are already enough convoluted options in the Chicken exporter - not sure making it more intimidating to newbies is worth it for something experienced coders can fix in a single line of code!

I think it is impossible to have different materials with the same name, so I don’t see how keeping the number can serve name uniqueness. If we have an object called “House” and material called “Window”, the name of the group “House_Window” is unique whether the index is kept or not.

Then, I would like to highlight that using underscores is not required either (despite to what Chicken says in line 902: “self.name = name.replace(’ ', ‘_’) # egg files don’t like spaces in names”). In fact, names may contain spaces if it is given in quotes.
The following egg is perfectly legal, check it:

<CoordinateSystem> { Z-up }

<Comment> { "Egg laid by Chicken for Blender vR44_nf" }

<Material> "Green Material" {
  <Scalar> diffr {0.0}
  <Scalar> diffg {1.0}
  <Scalar> diffb {0.0}
  <Scalar> specr {0.25}
  <Scalar> specg {0.25}
  <Scalar> specb {0.25}
  <Scalar> shininess {12.5}
}
<Material> Red {
  <Scalar> diffr {1.0}
  <Scalar> diffg {0.0}
  <Scalar> diffb {0.0}
  <Scalar> specr {0.25}
  <Scalar> specg {0.25}
  <Scalar> specb {0.25}
  <Scalar> shininess {12.5}
}
<Group> "My Cube" {
  <VertexPool> "My Cube Vertex_Pool" {
    <Vertex> 0 {
      1.0 0.999999940395 -1.0
    }
    <Vertex> 1 {
      1.0 -1.0 -1.0
    }
    <Vertex> 2 {
      -1.00000011921 -0.999999821186 -1.0
    }
    <Vertex> 3 {
      -0.999999642372 1.00000035763 -1.0
    }
    <Vertex> 4 {
      1.00000047684 0.999999463558 1.0
    }
    <Vertex> 5 {
      -0.999999940395 1.0 1.0
    }
    <Vertex> 6 {
      -1.00000035763 -0.999999642372 1.0
    }
    <Vertex> 7 {
      0.999999344349 -1.00000059605 1.0
    }
    <Vertex> 8 {
      1.0 0.999999940395 -1.0
    }
    <Vertex> 9 {
      1.00000047684 0.999999463558 1.0
    }
    <Vertex> 10 {
      0.999999344349 -1.00000059605 1.0
    }
    <Vertex> 11 {
      1.0 -1.0 -1.0
    }
    <Vertex> 12 {
      1.0 -1.0 -1.0
    }
    <Vertex> 13 {
      0.999999344349 -1.00000059605 1.0
    }
    <Vertex> 14 {
      -1.00000035763 -0.999999642372 1.0
    }
    <Vertex> 15 {
      -1.00000011921 -0.999999821186 -1.0
    }
    <Vertex> 16 {
      -1.00000011921 -0.999999821186 -1.0
    }
    <Vertex> 17 {
      -1.00000035763 -0.999999642372 1.0
    }
    <Vertex> 18 {
      -0.999999940395 1.0 1.0
    }
    <Vertex> 19 {
      -0.999999642372 1.00000035763 -1.0
    }
    <Vertex> 20 {
      1.00000047684 0.999999463558 1.0
    }
    <Vertex> 21 {
      1.0 0.999999940395 -1.0
    }
    <Vertex> 22 {
      -0.999999642372 1.00000035763 -1.0
    }
    <Vertex> 23 {
      -0.999999940395 1.0 1.0
    }
  }
  <Group> Cube_Red {
    <Polygon> {
      <MRef> { Red }
      <Normal> { 0.000000 -0.000000 1.000000 }
      <VertexRef> { 4 5 6 7 <Ref> { "My Cube Vertex_Pool" } }
    }
    <Polygon> {
      <MRef> { Red }
      <Normal> { -0.000000 -1.000000 -0.000000 }
      <VertexRef> { 12 13 14 15 <Ref> { "My Cube Vertex_Pool" } }
    }
    <Polygon> {
      <MRef> { Red }
      <Normal> { -1.000000 0.000000 -0.000000 }
      <VertexRef> { 16 17 18 19 <Ref> { "My Cube Vertex_Pool" } }
    }
  }
  <Group> "Cube Green" {
    <Polygon> {
      <MRef> { "Green Material" }
      <Normal> { 0.000000 0.000000 -1.000000 }
      <VertexRef> { 0 1 2 3 <Ref> { "My Cube Vertex_Pool" } }
    }
    <Polygon> {
      <MRef> { "Green Material" }
      <Normal> { 1.000000 -0.000000 0.000000 }
      <VertexRef> { 8 9 10 11 <Ref> { "My Cube Vertex_Pool" } }
    }
    <Polygon> {
      <MRef> { "Green Material" }
      <Normal> { 0.000000 1.000000 0.000000 }
      <VertexRef> { 20 21 22 23 <Ref> { "My Cube Vertex_Pool" } }
    }
  }
}

I believe the Chicken should rather use quotes then replacing spaces with underscores. This simply gives more flexibility and goes imply unneeded limitation.

The material groups in the egg file are a direct copy of the material groups feature in Blender (i.e. wiki.blender.org/index.php/Manua … _Materials), which certainly allows you to assign the same material to two different groups on the same mesh, so whilst they are refering to the same material you could have two groups with the same name if using material alone for uniqueness - the index has to stay really even though its a fairly esoteric scenario. (I can actually think of a use of that scenario though - providing multiple versions of geometry to be randomly selected from for variety as in the case of geometry to be used by AI, i.e. floor to walk on etc.) (Yes, you could merge those geometry groups and lose the index, but that is something for the user to explicitly do I think.)

Changing the naming system to allow spaces is a definite though.

I continue exploring eggs and chicken, and here is another bug report :slight_smile:
When writing matrices Chicken forgets about one indentation level (it is not critical error, though):

  <Transform> {
  <Matrix4> {
    1.000000 0.000000 0.000000 0.000000
    0.000000 1.000000 0.000000 0.000000
    0.000000 0.000000 1.000000 0.000000
    -1.000000 0.000000 0.000000 1.000000
  }
  }

The correct should be:

  <Transform> {
    <Matrix4> {
      1.000000 0.000000 0.000000 0.000000
      0.000000 1.000000 0.000000 0.000000
      0.000000 0.000000 1.000000 0.000000
      -1.000000 0.000000 0.000000 1.000000
    }

As reference I used Roaming Ralph world model.

PS: I would like to help re-writing Chicken, if possible. But the mess in the current code scares me :smiling_imp:
And due to the upcoming overhaul of Blender API in version 2.50 it is not reasonable to do now, what do you think?

Ok, psiberpunk has got back to me and I now have administrative access to the sourceforge project - if people have patches for Chicken feel free to send them my way or post them here.

I have uploaded all my changes and added a R51 release to the download page (Its the same as the file in my previous post, just with a more official version name.) As a reminder the sourceforge project page is sourceforge.net/projects/chicken-export/

I also made it explicit that version 1 is depreciated, as we get a lot of people using that version when the newer version is preferable despite the bug risk. That is all I can do today I’m afraid. Not sure when I’ll get to look at all the above bugs but hopefully within the next week or two. (That indentation bug hardly matters - the indentation is entirely optional and just for human readability, its a tiny fix though so might do it anyway.)

I agree that a re-write is in order, but I’m not a real fan of the start again approach - would rather incrementally clean the current code replacing it a bit at a time until its neat, even if that means temporary abstraction layers. A re-write tends to resurrect all the bugs and bad features that have been previously squashed. I’ld agree that doing it prior to 2.50 would be silly, and would certainly appreciate the help, but for now might clean up some of the non-gui stuff and at least start commenting it in more detail.

Good to see Chicken in active development again. Just wanted you to know that I’ve been using the new chicken and I’ve had no problems so far although I haven’t been exporting anything really fancy.

Well, there is a bug at line 710 in “chicken_exporter.py” file.

If we have an object with an armature modifier but without animations, Python returns this error:

RuntimeError: property not found in group.

I’ve patched this with these lines:

elif 'chicken_ani' in mesh.properties:
    del mesh.properties['chicken_ani']

Ok, now: I’ve planned to add some important features for my project, so can i commit changes to the SVN repo?

My personal roadmap is:

  1. Add texture filtering support.
  2. Add library link/append support (so we can use Blender as level editor for Panda3D).

Zuck,

Thanks - I’ve added that change. (Not yet commited to svn, I’ve got some other changes in there that I need to test before doing that - earlier stuff in this thread.)

If you want to make changes just send me a diff file or post it to this forum - I’ll then apply them to the repository.

I can see how you would do multi-texturing but I’m more curious about the library link/append support idea. I have been thinking about how one would use Blender as a level editor myself - my general thinking is to export an egg file with empty objects for game entities and geometry for the level itself. You already have the ability to export geometry that is invisible, for collision or AI purposes. The game code can then replace the empties using search and replace from their name or properties, and add any behavioural code etc - you can already do that but the objects appear empty in blender itself, which isn’t what you want. So my thinking is that a simple property that you tag an object with would make the exporter replace that object with an empty with the same name - you then apply that tag to the original model and each time you import it, or copy it, regardless of technique, the exporter acts correctly. The nice thing about that idea is its really easy to implement - a few lines of code, and very flexible. Its quite involved for the user however, especially the programmer. My delay in doing it is due to the need to write a tutorial and provide test code to show how to then load the level.

On top of the above I also think that the ability to export camera animations would be good for in game cut-scenes etc. - I was thinking two paths - one for position, the other for direction, with distance from the position proportional to focal length. Unless Panda has a better representation, but I haven’t had the time to look.
No idea if lights can be stuck in egg files, but I presume so.
That still leaves the issue of exporting a graph in 3D space, as often used for AI’s to walk along the edges (All the Unreal Tournament games do that for instance.) - don’t think egg files have edges support, and not sure what would happen if I throw 2 vertex ‘faces’ at it. Need to do some experimenting there.

That is my thoughts, so what are yours? Bare in mind that I’ve tried to keep mine quite general, so they are applicable to many types of game. I would prefer to avoid stuff that is overly specific to any one game type, and if added would certainly want such a feature disabled by default.

P.S. Thanks ZeroByte!

You could just export a NURBS curve and load it as motion path in panda.

Problem with that is it doesn’t encode focal length. I know there will be many people who are happy to keep that constant and just code it in, but I would rather make that data driven.

Hi Lethe :slight_smile:

First of all: we must replace all tabs with blank spaces (2 or 4) because tabs are really a pain :wink:

I’m sorry, for “texture filtering” I mean min/mag filters and mipmap option :wink:

This is the code snippet for Texture class constructor (I can post the diff file but with tab replace it is 2600 lines long):

self.mipmap = True
self.filter = 'linear'
if mtex is not None:
     # ...
     # NormalMap and envtype code
     # ...
     self.mipmap = (BTexture.ImageFlags.MIPMAP & mtex.tex.imageFlags)
     if not BTexture.ImageFlags.INTERPOL & mtex.tex.imageFlags:
         self.filter = 'nearest'
     if self.mipmap:
         self.filter = self.filter + '_mipmap_' + self.filter        

And then put these lines in write routine after envtype writing code:

filter_name = self.filter.upper()
ln.append('  <Scalar> minfilter { %s }'%filter_name)
ln.append('  <Scalar> magfilter { %s }'%filter_name)

For Link feature my idea is very simple:

# We check for an empty object with Dupli properties (i.e. DupliGroup):
grp = empty.DupGroup

# Object has DupliGroup property.
if grp is not None:
        
    # Append DupliGroup to the output file.
    l = Link(empty, grp.lib)
    l.write(buffer)

# Where Link class is:
class Link(Group):
    def __init__(self, obj, lib):
        Group.__init__(self, mesh=obj)
        self.lib = lib
        
    def convertFileNameToPanda(self, filename):
        path =  filename.replace('//', './').replace('\\', '/')
        if os.name == 'nt' and path.find(':') != -1:
            path = '/'+ path[0].lower() + path[2:]
        return path

    def write(self, buffer):
        buffer.append('<Instance> %s {'% self.name)
        self.writeInner(buffer)
        if self.lib is not None:
            buffer.append('  <File> { %s }' % (self.convertFileNameToPanda(self.lib.replace('.blend', ''))))
        buffer.append('}')

So the resulting egg file is something similar to:

<CoordinateSystem> { Z-up }

<Comment> { "Egg laid by Chicken for Blender v$RELEASE_VERSION" }

<Instance> dupli_object {
  <Transform> {
    <Matrix4> {
      0.225743 1.280250 0.000000 0.000000
      -0.492404 0.086824 0.000000 0.000000
      0.000000 0.000000 -0.100000 0.000000
      0.000000 542.607239 0.000000 1.000000
    }
  }
  <ObjectType> { model }
  <File> { ./linked_egg_file }
}

Obviously we need to export “linked_egg_file.egg” manually for now, but in case we can export linked files with only one call to export function in the future.

for Append, simply use a snippet like this:

dups = empty.DupObjects

if len(dups) > 0:
    for d in dups:
        data = d[0]
        matrix = d[1]
                        
        # Create dupli objects.
        type = data.getType()
        new_obj = Blender.Object.New(type)
        new_obj.shareFrom(data)
        new_obj.setMatrix(matrix)
        
        # write "new_obj" to output file
        # ...

It works, I’ve already tested it. If you commit last changes to SVN repo I can prepare a diff file with Append/Link feature implementation and then post it here :slight_smile:

Camera and lights implementation is a big trouble because egg syntax currently doesn’t support them :frowning:

Zuck, looks good.

Good on the filters - that is a must have for sure.
The append feature doesn’t cover all cases, most notably the case for stand in geometry for something that is not necessary visible. Spawn points for instance. Or stuff that is different each time. However, the case of replicated geometry from an external file is both common and well handled, so will certainly add that in as well.

I agree on the tabs. For integrating that code however it looks like you’ve pasted it all anyway, at least I can’t see any omissions other than position, which I can infer easily enough, so I’ll just integrate it from your post. Expect some more comments though - I’m trying to at least make sure the new stuff is extremely well commented!

I’ll try and get it all done this weekend - should be able to find the time.

For lights that is a problem. Could always hack it of course - effectively convert lights to empties with the parameters in properties. For cameras my only desire is to get the animation information in, to be played back pragmatically when needed, and whilst some coding would be required to understand it that is hackable enough if not ideal. Could provide some code for handling those cases in the manual though - the camera case could be wrapped up all neatly as an interval.

Good! :slight_smile: Thanks!

This is another small “bug” fix for exported texture names (which are not equal to Blender ones). I think the line is the number 824 but I’m not sure because of my previous changes.

Replace:

name = self.name
if texcount > 1: 					
    name += str(i)

with:

name = texSpec[0].tex.getName()

Another fix, in my opinion, is required for Material management: currently the exporter writes a material description only if it is linked to a mesh data instead of an object data (link a material to an object is more common in Blender work-flow)…What do you think about this?

Yes, it’s a reasonable workaround :slight_smile:

Its all implemented at this point and passed all regression tests and tests for all new stuff, except for the empty/instancing thing, which is refusing to call the Link write method for some reason - so much for inheritance! (I made a lot of changes though - allowed instances to be put into the hierarchy of objects and gave it a more sophisticated filename determination method to improve flexibility, so its not very similar to your above code any more. But I’ll get that bug soon enough…)

What exactly is the bug your fixing with that above code fragment though? Don’t feel like working it out myself right now as brain is focused on solving the above problem and would rather not distract myself too much.

As for the material thing are you sure? It certainly exports them as I expect without problem and I don’t think I use Blender in an a-typical way!

May I report a ‘bug’ (rather, an annoyance):
Chicken never uses the default texture stage, which can be pretty frustrating if working with texture replacement in panda. May I propose that an uv name of “UVTex” (the default in blender) gets translated to the default stage in Panda3D? (I believe thats an empty string in the entry, and omission of the uv-name scalar)

Great! :wink:

It’s a very small fix for egg texture names: currently Chicken selects the name based on material name and texture index (in the material texture list). With that fix, instead, Chicken selects the same name the texture has in Blender texture list.

For example:

I think is more natural to link a material to an object instead of a mesh (in Blender), isn’t it? :slight_smile:

Zuck - I’m afraid that making your sugested texture naming change is not sensible. In blender a texture is independent from its uv mapping, but in the egg file format they are connected - i.e. each texture specifies the name of the UV coordinates to use. As textures need unique names doing what you sugest would fail if multiple objects used the same texture with differently named UV coordinates - not a comon case but a case that can come up. I have however changed the naming convention to include the material name so you can programatically search by that.
I’m still unsure what your saying about material linking and have left it as is - what I do know is the current system works fine for me and a lot of other people.
Read the changelog to see how I’ve extended to the DupliGroup linking - it will still do what you wanted in the original scenario, but I’ve made it more flexible - primarilly in that it will allow you to override the filename by a game engine property or use the group name, so it will work for groups that are in the same file rather than linked groups only. It also has hierachy support, so making hierachies of instances or sticking them in hierachies of normal objects, or even normal objects under instances, should all work.

pro-rsoft - that change is in there.

I’ve done a new release on the sourceforge site - if people can please test it and provide any feedback that would be great.
The changelog is:

  • Spaces are now allowed in names - in principal for everything, not just objects. (Haven’t tested everything, but objects, materials and textures all work fine.)
  • Changed sub-group for multi-material objects naming scheme. Instead of [object name][index] its now [object name][index][material name]. [index] is formated to always be two digits, i.e. 00, 01, 02 etc. I returned it to being zero based too, as programers will be the once interfacing with this.
  • Similar to above for texture names. They are now called [material name][index][texture name]. Index is again always 2 characters.
  • Enabled curve support by default. Put in a warning if you use an unsuported curve type. (Must be NURBS and can’t loop.)
  • Made 2 spaces the standard indentation for the main script, cleaned up indentation where screwy.

Below as provided by zuck:

  • Fixed bug for meshes with armitures but no animations entered.
  • Support for mip-mapping setting in blender - will copy from blenders texture options.
  • Dupligroup emptys are now replaced by file references. To decide the filename it goes down a set of checks and uses the first that works - first it checkes all objects in the group, if any have a ‘filename’ property (From the game engine tab) that is used, after conversion from being a blender style filename. Second it checks to see if the group is in an external library - if so it uses the filename of the external library without the .blend. Finally, if both of the previous fail it simply uses the groups name.

Below as sugested by pro-rsoft:

  • Any uv texture called UVTex will be given an empty name, to make it the default texture stage.

Yes, you’re right! :wink: So forget my last bug fix :stuck_out_tongue:

Good job! The script works and rocks! :smiley:

My next personal roadmap:

  1. Add extend/clamp/repeat texture attribute support.
  2. Add possibility to include (append) linked data as concrete objects, opposed to new link feature (with a new button switch in the GUI).
  3. Add DupliFrames/DupliVertices instantiation support (linked or appended).

Is it ok?

Sounds good to me!
Better texture support is always helpful, and #3 makes perfect logical sense, though your going to need 2 extra classes for it. Just keep an eye on how I did 'em - will want to keep the hierarchy. For #2 may I suggest extending the link class - simply add code that will, if the ‘include external data’ flag is set, add all the objects in the group as children of the Link class, and then make write call the Object write method - that is neat and makes sure you support the fact that multiple objects can go into a group. Of course, if you can think of a better way go for it, but that way you only need about 10 more lines or so, ignoring the tedium that is the gui, and the other classes you will add to get support for #3.

This is the patch for feature #1:

Index: chicken_export.py
===================================================================
--- chicken_export.py	(revision 53)
+++ chicken_export.py	(working copy)
@@ -6,7 +6,7 @@
 Group: 'Export'
 Tip: 'Export to the Panda3D Egg format.'
 """
-__author__ = "Daniel Amthauer, Simon Groenewolt, Tom SF Haines"
+__author__ = "Daniel Amthauer, Simon Groenewolt, Tom SF Haines, Emanuele Bertoldi"
 __url__ = ("Chicken homepage, http://chicken-export.sf.net")
 __email__="daniel*amthauer:gmail*com"
 __version__ = "$RELEASE_VERSION"
@@ -900,6 +900,7 @@
     self.envtype = 'modulate'
     self.mipmap = True
     self.filter = 'linear'
+    self.wrap = {'u': 'repeat', 'v': 'repeat', 'w': 'repeat'}
     
     if mtex:
       # Detect if its a usable normal map...
@@ -915,6 +916,24 @@
         self.filter = 'nearest'
       if self.mipmap:
         self.filter = self.filter + '_mipmap_' + self.filter
+        
+      # Extract the wrapping mode...
+      wrap_mode = mtex.tex.extend
+      if wrap_mode == (BTexture.ExtendModes.EXTEND):
+        self.wrap['u'] = self.wrap['v'] = self.wrap['w'] = 'clamp'
+      elif wrap_mode in [BTexture.ExtendModes.CLIP, BTexture.ExtendModes.CLIPCUBE]:
+        self.wrap['u'] = self.wrap['v'] = self.wrap['w'] = 'border_color'
+      tflags = mtex.tex.flags
+      umir = BTexture.Flags.REPEAT_XMIR & tflags
+      vmir = BTexture.Flags.REPEAT_YMIR & tflags
+      if umir or vmir:
+          # When we select "mir" option for a texture component in Blender,
+          # the other one is extended (clamp). I know: it's not logical...
+          self.wrap['u'] = self.wrap['v'] = self.wrap['w'] = 'clamp'
+          if umir:
+              self.wrap['u'] = 'mirror'
+          if vmir:
+              self.wrap['v'] = 'mirror'
 
   def setAlphaChannel(self, alphaFileName = None):
     self.hasAlpha = True
@@ -932,13 +951,22 @@
     if self.hasAlpha:
       if self.alphaFileName:
         ln.append('  <Scalar> alpha-file { "%s" }'%self.alphaFileName)
-      ln.append('  <Scalar> alpha { blend_no_occlude }')
-      ln.append('  <Scalar> format { rgba }')
+      ln.append('  <Scalar> alpha { BLEND_NO_OCCLUDE }')
+      ln.append('  <Scalar> format { RGBA }')
       ln.append('  <Scalar> draw_order { 10000 }')
-    ln.append('  <Scalar> envtype { %s }'%self.envtype)
+    ln.append('  <Scalar> envtype { %s }'%self.envtype.upper())
     filter_name = self.filter.upper()
     ln.append('  <Scalar> minfilter { %s }'%filter_name)
-    ln.append('  <Scalar> magfilter { %s }'%filter_name) 
+    ln.append('  <Scalar> magfilter { %s }'%filter_name)
+    wrapu = self.wrap['u'].upper()
+    wrapv = self.wrap['v'].upper()
+    wrapw = self.wrap['w'].upper()
+    if wrapu == wrapv == wrapw:
+        ln.append('  <Scalar> wrap { %s }'%wrapu)
+    else:
+        ln.append('  <Scalar> wrapu { %s }'%wrapu)
+        ln.append('  <Scalar> wrapv { %s }'%wrapv)
+        ln.append('  <Scalar> wrapw { %s }'%wrapw)
     ln.append('}')
     buffer.extend(ln)
 

There are some differences between Blender mirror option and Panda3D one but, in my opinion, the result is satisfactory. :slight_smile:

I’ve realized a test file for these features, can i send it to your email address?