Shape Files: Import & Export Blender Meshes
Moderators: jelco, bert_the_turtle
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.
-
- level1
- Posts: 40
- Joined: Mon May 07, 2007 4:35 pm
...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?
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.
-
- level1
- Posts: 40
- Joined: Mon May 07, 2007 4:35 pm
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.
EDIT: Swizzle code fixed. Heading home now, and then to bed. I'll finish up the script tomorrow, then get started on the importer.
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:
P.P.P.S
You probably should check for any Object name *starting* with "Marker", so different types of markers can use their real Darwinia names.
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 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.
-
- level1
- Posts: 40
- Joined: Mon May 07, 2007 4:35 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:
New code:
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)
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.
... actually you still have an orientation issue:
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!
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!
- The GoldFish
- level5
- Posts: 3961
- Joined: Fri Mar 01, 2002 9:01 pm
- Location: Bowl / South UK
- Contact:
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!
-
- level1
- Posts: 40
- Joined: Mon May 07, 2007 4:35 pm
We're working from different angles...
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.
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.
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
-
- level1
- Posts: 40
- Joined: Mon May 07, 2007 4:35 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:
See the bug? It should be:
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?
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?
Who is online
Users browsing this forum: No registered users and 18 guests