Shape Files: Import & Export Blender Meshes

Talk about your new mod or map here

Moderators: jelco, bert_the_turtle

brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 4:36 am

Orientation is wrong and the object is laying down in the ground. Top now points North towards the Darwinia editor's origin. In Darwinia the NW corner is labeled X pointing East and Z pointing South. I don't want to try other offsets / rotations since the frame of ref will change when you fix the top orientation.
PaladinOfKaos
level1
level1
Posts: 40
Joined: Mon May 07, 2007 4:35 pm

Postby PaladinOfKaos » Tue May 08, 2007 4:46 am

OK, another test. No change to the up/front vectors, but the swizzling has changed. Same code as before...
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 4:48 am

...its ALWAYS in the last box! Now top faces East.

EDIT: I rotated the swizzle one more time to ZXY and everything seems perfect... for one Object. Left & Right in front view correspond to L/R when facing the Darwinia editor's origin.

BUT, if I duplicate the object and do some translations and rotations in the blender front view (ZX plane) the up & front don't change for the tranfromed object, and the shape fragment gets offset in the Darwinia XZ plane (ground plane). Mind you I only changed the swizzle of line 203, so I may have things out of sync somewhere... but the up & front seem hardcoded?
Last edited by brice on Tue May 08, 2007 5:12 am, edited 3 times in total.
User avatar
Shwart!!
level5
level5
Posts: 1237
Joined: Sun Nov 12, 2006 1:36 am

Postby Shwart!! » Tue May 08, 2007 4:51 am

And its time for a rousing game of face tag!!1!
Seriously awesome, by the way. Keep up the good work.

Shwart!!
PaladinOfKaos
level1
level1
Posts: 40
Joined: Mon May 07, 2007 4:35 pm

Postby PaladinOfKaos » Tue May 08, 2007 5:16 am

Up and front are still hardcoded, yes. I'm waiting until I can sit in front of a box with both Blender and Darwinia to work on that bit. And you do have something out of sync - the translations of the object also need to be swizzled. I'll fix the last entry to match your new swizzling in all locations it needs to be.

EDIT: Swizzle code fixed. Heading home now, and then to bed. I'll finish up the script tomorrow, then get started on the importer.
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 5:23 am

Ditto here. Great job!

The translation of Objects is still in the wrong plane. But I'm glad you'll be able to see it for yourself, since describing 3D orientations in writing is awkward!

Thanks for all your work!

-brice


P.S. One last thought: I picked the front orientation to line up with looking towards the XZ origin in the editor because that made sense to me. But it may not be what is right. If you place a StaticShape on a map, the (rx, rz) rotation vector starts as (1.0, 0.0). Maybe that helps sort out orientation of the axes?

P.P.S. Here's the format string for 3-place precision:

Code: Select all

s = s+' '+ "% .3f" % self.Positions[i][j]

prints:      112:  61.727 -15.867 -0.000


P.P.P.S
You now have a several objects, each with a unique object name, each a child of SceneRoot (Or whatever you re-parented them to), and each using the 'Marker' mesh. The script just has to check that the mesh is named 'Marker' to create a marker, otherwise it creates a fragment. A bit more complex, but it lets DupliVerts work, and you can represent markers however you like.

You probably should check for any Object name *starting* with "Marker", so different types of markers can use their real Darwinia names.
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 6:33 pm

brice wrote:P.S. One last thought: I picked the front orientation to line up with looking towards the XZ origin in the editor because that made sense to me. But it may not be what is right. If you place a StaticShape on a map, the (rx, rz) rotation vector starts as (1.0, 0.0). Maybe that helps sort out orientation of the axes?


I thought about it a little more, and it probably would be best if you can make an unrotated Object export with the standard header:

Code: Select all

      up:     0.00  1.00  0.00
      front:  0.00  0.00  1.00
      pos:    0.00   0.00   0.00


That way hand edits to coordinates would be in the familiar orientations. The map coords increase in the directions of the arrows in the editor: X East and Z South. In Blender Front View the Y axis points away from the viewer, while in the map editor the Z axis points towards the viewer when looking at the origin. So swizzling to get front to line up the way I was doing last night will probably introduce a -1.00 transformation somewhere.


EDIT: ...so I returned to the original YZX swizzle you had me try... I think I got the changes right for that:

Code: Select all

def createShpFragment(obj):
  new_shp = shp_fragment()
  new_shp.setName(obj.getName())
  parent = obj.getParent()
  if parent:
    new_shp.setParent(parent.getName())
  matrix = obj.getMatrix('localspace')
  new_shp.setPos(matrix[3][1],matrix[3][2],matrix[3][0])
  msh_name = obj.getData(name_only=True)
  msh = Blender.Mesh.Get(msh_name)
  for f in msh.faces:
    verts=[0,0,0]
    for i in range(3):
      col = new_shp.addColour(f.col[i].r,f.col[i].g,f.col[i].b)
      pos = new_shp.addPosition(f.verts[i].co.y,f.verts[i].co.z,f.verts[i].co.x)
      verts[i]=new_shp.addVertex(pos,col)
    new_shp.addTriangle(verts[0],verts[1],verts[2])
  return new_shp

def countParent(obj,count):
  parent = obj.getParent()
  if parent:
    return countParent(parent, count+1)
  else:
    return count

def createShpMarker(obj):
  new_shp = shp_marker()
  new_shp.setName(obj.getName())
  parent = obj.getParent()
  if parent:
    new_shp.setParent(parent.getName())
  new_shp.setDepth(countParent(obj,1))
  matrix = obj.getMatrix('localspace')
  new_shp.setPos(matrix[3][1],matrix[3][2],matrix[3][0])
  return new_shp


With this code, I export, then I hand-edit the front & up to the standard orientation above before loading into Darwinia. With these changes the edits in Top View are shown in the same screen orientations when viewed from above in the editor. I.e. when I drag a vertex to the lower right in blender, the shape shows the drag to the SE for an unrotated StaticShape.

Hope this helps!, but probably you can figure it out faster on your own.

-brice
Last edited by brice on Tue May 08, 2007 7:10 pm, edited 1 time in total.
PaladinOfKaos
level1
level1
Posts: 40
Joined: Mon May 07, 2007 4:35 pm

Postby PaladinOfKaos » Tue May 08, 2007 7:04 pm

My brain hurts. But I have gotten it working with the standard header. Translation and rotation work brilliantly. Empties are still the only way to make markers, I'll work on mesh-based markers soon. The y-axis is front, and the z-axis is up. I'm still deciding on exactly how I want to fix the color bug, and it still doesn't triangulate.

Stuff left to do:
  • Mesh-based markers
  • Triangulate
  • Default color export


New code:

Code: Select all

#!BPY

# """
# Name: 'Darwinia Shape File (.shp)'
# Blender: 243
# Group: 'Export'
# Tooltip: 'Export to the Shape format used by Darwinia'
# """
__author__ = 'Branan Riley'
__version__ = '0.9'
__email__ = 'branan@gmail.com'
__bpydoc__ = """\
This script exports to the Shape format used by Darwinia.

Every selected object will be exported as a fragment in
the shape file. If an object has a parent, that parent will
be defined as the parent of its fragment. Otherwise, a
fragment will be parented to 'SceneRoot'. Markers are not
yet supported, but it should be pretty easy, once I get
everything else done.


This program is distributed under the GPL. The complete
text of the GPL is availble at http://www.gnu.org/licenses/gpl.txt
"""

import Blender

mesh_objects = []
empty_objects = []

class shp_object:
  def __init__(self):
    self.ParentName='SceneRoot'
    self.Up=0.0,1.0,0.0
    self.Front=0.0,0.0,1.0
    self.Pos=0.0,0.0,0.0
    self.Name='NewDataChunk'
 
  def setParent(self, parent):
    self.ParentName=parent
 
  def setUp(self, x, y, z):
    self.Up=x,y,z
 
  def setFront(self, x, y, z):
    self.Front=x,y,z
 
  def setPos(self, x, y, z):
    self.Pos=x,y,z
 
  def setName(self, name):
    self.Name=name

class shp_fragment(shp_object):
  def __init__(self):
    shp_object.__init__(self)
    self.Positions=[]
    self.Colours=[]
    self.Vertices=[]
    self.Triangles=[]
 
  def addPosition(self, x, y, z):
    if self.Positions.count((x,y,z)) == 0:
      self.Positions.append((x,y,z))
    return self.Positions.index((x,y,z))
 
  def addColour(self, r, g, b):
    if self.Colours.count((r,g,b)) == 0:
      self.Colours.append((r,g,b))
    return self.Colours.index((r,g,b))
 
  def addVertex(self, pos, col):
    if self.Vertices.count((pos,col)) == 0:
      self.Vertices.append((pos,col))
    return self.Vertices.index((pos,col))
 
  def addTriangle(self, pos0, pos1, pos2):
    self.Triangles.append((pos0,pos1,pos2))
 
  def write(self, file):
    file.write('Fragment: '+self.Name+'\n')
   
    file.write('\tParentName: '+self.ParentName+'\n')
   
    s = '\tUp:'
    for i in range(3):
      s = s+' '+str(self.Up[i])
    s = s+'\n'
    file.write(s)
   
    s = '\tFront:'
    for i in range(3):
      s = s+' '+str(self.Front[i])
    s = s+'\n'
    file.write(s)
   
    s = '\tPos:'
    for i in range(3):
      s = s+' '+str(self.Pos[i])
    s = s+'\n'
    file.write(s)
   
    s = '\tPositions: '+str(len(self.Positions))+'\n'
    file.write(s)
    for i in range(len(self.Positions)):
      s = '\t\t'+str(i)+':'
      for j in range(3):
        s = s+' '+str(self.Positions[i][j])
      s = s+'\n'
      file.write(s)
   
    file.write('\tNormals: 0\n')
   
    s = '\tColours: '+str(len(self.Colours))+'\n'
    file.write(s)
    for i in range(len(self.Colours)):
      s = '\t\t'+str(i)+':'
      for j in range(3):
        s = s+' '+str(self.Colours[i][j])
      s = s+'\n'
      file.write(s)
   
    s = '\tVertices: '+str(len(self.Vertices))+'\n'
    file.write(s)
    for i in range(len(self.Vertices)):
      s = '\t\t'+str(i)+':'
      for j in range(2):
        s = s+' '+str(self.Vertices[i][j])
      s = s+'\n'
      file.write(s)
   
    s = '\tTriangles: '+str(len(self.Triangles))+'\n'
    file.write(s)
    for i in range(len(self.Triangles)):
      s = '\t\t'
      for j in range(2):
        s = s+str(self.Triangles[i][j])+' '
      s = s+str(self.Triangles[i][2])+'\n'
      file.write(s)

class shp_marker(shp_object):
  def __init__(self):
    shp_object.__init__(self)
    self.Depth = 1
 
  def setDepth(self, depth):
    self.Depth = depth
 
  def write(self, file):
    file.write('Marker: '+self.Name+'\n')
   
    file.write('\tParentName'+self.ParentName+'\n')
   
    s = '\tDepth: '+str(self.Depth)+'\n'
    file.write(s)
   
    s = '\tUp:'
    for i in range(3):
      s = s+' '+str(self.Up[i])
    s = s+'\n'
    file.write(s)
   
    s = '\tFront:'
    for i in range(3):
      s = s+' '+str(self.Front[i])
    s = s+'\n'
    file.write(s)
   
    s = '\tPos:'
    for i in range(3):
      s = s+' '+str(self.Pos[i])
    s = s+'\n'
    file.write(s)
   
    file.write('MarkerEnd\n')
 
def getObjects():
  global mesh_objects
 
  selected_obs = Blender.Object.GetSelected()
  for obj in selected_obs:
    type = obj.getType()
    if type == 'Mesh':
      mesh_objects.append(obj)
    elif type == 'Empty':
      empty_objects.append(obj)

def createShpFragment(obj):
  new_shp = shp_fragment()
  new_shp.setName(obj.getName())
  parent = obj.getParent()
  if parent:
    new_shp.setParent(parent.getName())
  matrix = obj.getMatrix('localspace')
  new_shp.setPos(-1.0*matrix[3][0],matrix[3][2],matrix[3][1])
  new_shp.setFront(-1.0*matrix[1][0],matrix[1][2],matrix[1][1])
  new_shp.setUp(-1.0*matrix[2][0],matrix[2][2],matrix[2][1])
  msh_name = obj.getData(name_only=True)
  msh = Blender.Mesh.Get(msh_name)
  for f in msh.faces:
    verts=[0,0,0]
    for i in range(3):
      col = new_shp.addColour(f.col[i].r,f.col[i].g,f.col[i].b)
      pos = new_shp.addPosition(f.verts[i].co.x,f.verts[i].co.z,f.verts[i].co.y)
      verts[i]=new_shp.addVertex(pos,col)
    new_shp.addTriangle(verts[0],verts[2],verts[1])
  return new_shp

def countParent(obj,count):
  parent = obj.getParent()
  if parent:
    return countParent(parent, count+1)
  else:
    return count

def createShpMarker(obj):
  new_shp = shp_marker()
  new_shp.setName(obj.getName())
  parent = obj.getParent()
  if parent:
    new_shp.setParent(parent.getName())
  new_shp.setDepth(countParent(obj,1))
  matrix = obj.getMatrix('localspace')
  new_shp.setPos(-1.0*matrix[3][0],matrix[3][2],matrix[3][1])
  new_shp.setFront(-1.0*matrix[1][0],matrix[1][2],matrix[1][1])
  new_shp.setUp(-1.0*matrix[2][0],matrix[2][2],matrix[2][1])
  return new_shp

def mainFunc(filename):
  file = open(filename, 'w')
  getObjects()
  shp_fragments = []
  shp_markers = []
  for obj in mesh_objects:
    shp_fragments.append(createShpFragment(obj))
  for obj in empty_objects:
    shp_markers.append(createShpMarker(obj))
  for frag in shp_fragments:
    frag.write(file)
  file.write('\n')
  for mrk in shp_markers:
    mrk.write(file)
  file.close()

fname = Blender.sys.makename(ext='.shp')
Blender.Window.FileSelector(mainFunc, "Export SHP File", fname)
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 7:27 pm

Works great! I edited my post above just as you posted the new code. I think things are fine the way you have them. In the Darwinia editor I have to place the camera so it faces away from the map origin in order to get top view edits to match up. But that's no problem. Just a consequence of the axes directions in blender and darwinia.
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Tue May 08, 2007 8:34 pm

... actually you still have an orientation issue:
Image
Image

The blender image is Top View, and the XZ axes in the darwinia image are according to the editor. The duplicate Object was rotated in the Front View plane. Note especially that the upright Objects (showing only their tops) are mirrored in these two images -- they're not in the same orientations either.

Too bad we can't just tell blender to use darwinia's axes. If you can't make it work with the standard export header, then drop that idea. The handful of people who would want to hand-edit the exported files (like me) can figure out the right axes to edit. I didn't realize it would be so painful. Sorry!
User avatar
The GoldFish
level5
level5
Posts: 3961
Joined: Fri Mar 01, 2002 9:01 pm
Location: Bowl / South UK
Contact:

Postby The GoldFish » Tue May 08, 2007 10:44 pm

Surely you can convert from XYZ to XZY by swapping Z and Y, and inverting X (you might want to do some more stuff to get the orientation right though.
-- The GoldFish - member of former GIT and commander in chief of GALLAHAD. You could have done something, but it's been fixed. The end. Also, play bestgameever!
PaladinOfKaos
level1
level1
Posts: 40
Joined: Mon May 07, 2007 4:35 pm

Postby PaladinOfKaos » Wed May 09, 2007 4:03 pm

We're working from different angles...

Code: Select all

  Z          Y              Y
  |  Y       |  X           |  Z
  | /        | /            | /
  |/         |/             |/
  0---X      0---Z      X---0
(Blender)  (Darwinia)  (Darwinia)
              (Me)        (You)


Either way, it looks like there's an issue with an object that is both translated and rotated. If what I've seen the exporter do so far is indicative, the right object in the blender view is the bottom object in Darwinia view. The translation got rotated along with the rest of the object. I ought to be able to do <b_pos>*(1/[b_rot]) before exporting the position. Hopefully, that will fix it.

The header is generated from Blender's matrix now, so I can try different swizzles. I've almost got it working, though. Translation and rotation are good, just not both at once.
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Wed May 09, 2007 7:52 pm

PaladinOfKaos wrote:If what I've seen the exporter do so far is indicative, the right object in the blender view is the bottom object in Darwinia view.


Depends on whether you're talking my view or yours. In the pics above, the upside down Object is above the one which shows only it's top. Seen from Front View in blender, the upright Object is at the origin and the transformed Object is translated up and to the right in the Front plane, and rotated. I think I did the ops as so: drag copy along the blender X axis, rotate around cursor (at origin), then rotate object in place. So in my Darwinia pic, the Z-most object is above the one which is closer to the origin.

When you do your own testing, you should use a chiral object so you can easily see mirrorings like these. I didn't notice the problem until I played with this junk object and dragged a bunch of points randomly before duplicating and transforming the object.

In any case I'll re-orient my cameras and screenshots to match your Darwinia axes, as you ascii'd them above.

-brice
PaladinOfKaos
level1
level1
Posts: 40
Joined: Mon May 07, 2007 4:35 pm

Postby PaladinOfKaos » Wed May 09, 2007 8:31 pm

I found the bug... It had nothing to do with the 'front' and 'up' vectors at all... I had a rather stupid bug on line 205:

Code: Select all

pos = new_shp.addPosition(f.verts[i].co.x,f.verts[i].co.z,f.verts[i].co.y)

See the bug? It should be:

Code: Select all

pos = new_shp.addPosition(-1.0*f.verts[i].co.x,f.verts[i].co.z,f.verts[i].co.y)


I've also switched to a format-string based conversion, so floats are restricted to 3 digits after the decimal.

I'm going to create another thread that will always have the most recent code in the first post. It'll be easier for others to find the script that way. I've really only got two things left to implement (triangulation and mesh-based markers), then I can work on the import script. The conversations on that script can stay in this thread until it's done, then it'll get added to the release thread. Sound good to everyone?
brice
level2
level2
Posts: 167
Joined: Fri Feb 23, 2007 6:04 am

Postby brice » Wed May 09, 2007 8:45 pm

Sounds fine to me. Except why not move all the script talk there? I don't mind if this thread dies. Once we get a working solution, I (or we or someone) will probably create yet another thread with how-tos and so on.

But it's your lead.

-brice

Return to “Mod Projects”

Who is online

Users browsing this forum: No registered users and 18 guests