remove trailing white space
[io_import_off.git] / io_import_off.py
1 # io_import_off.py Stefan Huber
2 #
3 # Import DEC Object File Format (*.off)
4
5
6 bl_info = {
7 "name": "Import DEC Object File Format (.off)",
8 "author": "Stefan Huber (shuber2@gmail.com)",
9 "version": (0,0),
10 "blender": (2, 5, 57),
11 "api": 31667,
12 "location": "File > Import > Import DEC Object File Format (.off)",
13 "description": "Import DEC Object File Format (.off) files",
14 "warning": "",
15 "category": "Import-Export"
16 }
17
18 """
19 This script imports DEC Object File Format files to Blender.
20
21 The DEC (Digital Equipment Corporation) OFF format is very old and
22 almost identical to Wavefront's OBJ. I wrote this so I could get my huge
23 meshes into Moonlight Atelier. (DXF can also be used but the file size
24 is five times larger than OFF!) Blender/Moonlight users might find this
25 script to be very useful.
26
27 Usage:<br>
28 Execute this script from the "File->Import" menu and choose an OFF file to
29 open.
30
31 Notes:<br>
32 Port to blender 2.5 beta 2. - shuber
33 UV Coordinate support has been added. - Scorpius
34 FGON support has been added. - Cam
35 New Mesh module now used. - Cam
36 """
37
38 __version__ = '.'.join([str(s) for s in bl_info['version']])
39
40
41
42
43 import bpy
44 from bpy.props import *
45
46
47
48 def isComment(line):
49 """Is this line a comment line?"""
50 line = line.strip()
51 if len(line) == 0:
52 return False
53 if line[0] == "#":
54 return True
55 return False
56
57
58 def getNextLine(f):
59 """Read next line from file 'f' and ignore comments"""
60 line = f.readline().strip()
61 while isComment(line):
62 line = f.readline().strip()
63 return line
64
65
66
67 def unpack_vertices( vertices ):
68 l = []
69 for t in vertices:
70 l.extend(t)
71 return l
72
73
74 def unpack_faces(faces):
75
76 l = []
77 for face in faces:
78
79 # Build triangle fans
80 for k in range(1, len(face)-1):
81
82 tri = [face[0], face[k], face[k+1]]
83
84 # Rotate triangle, such that last index is not zero
85 if tri[2] == 0:
86 tri = [ tri[2], tri[0], tri[1] ]
87
88 l.extend(tri)
89 l.extend([0])
90
91 return l
92
93
94 def importFile(filepath, context):
95
96 # List of vertices, a vertex is a 3-tuple (x,y,z)
97 vertices = []
98 # List of faces, a face is a list of indices within vertices
99 faces = []
100
101 try:
102
103 f = open(filepath, "r")
104
105 # Get the header line
106 line = getNextLine(f)
107 if line != "OFF":
108 print("Error: header line does not start with 'OFF'.")
109 print(line)
110 return False
111
112 #Get number of vertices, faces and edges
113 line = getNextLine(f).split()
114 if len(line) < 2:
115 print("Error: Line of number of vertices, faces and edges is invalid.")
116 return False
117 [numVertices, numFaces] = map(int, line[0:2])
118
119 if numVertices < 0:
120 print("Error: Number of vertices is negative!")
121 return False
122 if numFaces < 0:
123 print("Error: Number of faces is negative!")
124 return False
125
126 # Get all vertices
127 for n in range(numVertices):
128 line = getNextLine(f).split()
129 if len(line) < 3:
130 print("Error: to few coordinates for vertex", n)
131
132 # Add the vertex
133 vertices += [ list(map(float, line[0:3])) ]
134
135 # Get all faces
136 for n in range(numFaces):
137 line = getNextLine(f).split()
138
139 # Get number of vertices
140 lenFace = int(line[0])
141 line = line[1:]
142 if len(line) < lenFace:
143 print("Error: to few vertices for face", n)
144
145 # Add the face
146 faces += [ list(map(int, line[0:lenFace])) ]
147
148 print("Finish reading file. Got %d vertices, %d faces." % \
149 (len(vertices), len(faces)) )
150
151 # Add a mesh
152 me = bpy.data.meshes.new("Mesh")
153
154 vertexdata = unpack_vertices(vertices)
155 facedata = unpack_faces(faces)
156
157 # Add given number of vertices and faces
158 me.vertices.add( len(vertexdata)//3 )
159 me.faces.add( len(facedata)//4 )
160 me.vertices.foreach_set("co", vertexdata)
161 me.faces.foreach_set("vertices_raw", facedata)
162 me.update()
163
164 ob = bpy.data.objects.new("Mesh", me)
165 ob.data = me
166 scene = context.scene
167
168 scene.objects.link(ob)
169 scene.objects.active = ob
170
171 except Exception as e:
172 print("Error reading .off file")
173 print(e)
174 return False
175
176
177
178 class IMPORT_OT_dec_off(bpy.types.Operator):
179 '''Import DEC object file format (.off) files as specified by
180 http://shape.cs.princeton.edu/benchmark/documentation/off_format.html'''
181
182 bl_idname = "import_scene.dec_off"
183 bl_label = "Import DEC off"
184 bl_description = "Imports DEC object file format (.off)"
185 bl_options = {'REGISTER'}
186
187 filepath = StringProperty(name="File Path",
188 description="Filepath used for importing the file",
189 maxlen=1024,
190 default=""
191 )
192
193 extEnum = [
194 ('*', 'All image formats', 'Import all know image (or movie) formats.'),
195 ('off', 'OFF (.off)', 'Object File Format') ]
196
197 extension = EnumProperty(name="Extension",
198 description="Only import files of this type.",
199 items=extEnum )
200
201 def execute(self, context):
202
203 # File Path
204 filepath = self.properties.filepath
205 # Call Main Function
206 importFile(filepath, context)
207
208 return {'FINISHED'}
209
210 def invoke(self, context, event):
211 wm = bpy.context.window_manager
212 wm.fileselect_add(self)
213
214 return {'RUNNING_MODAL'}
215
216
217 # Registering / Unregister
218 def menu_func(self, context):
219 self.layout.operator(IMPORT_OT_dec_off.bl_idname,
220 text="DEC Object File Format (.off)",
221 icon='PLUGIN' )
222
223 def register():
224 bpy.utils.register_module(__name__)
225 bpy.types.INFO_MT_file_import.append(menu_func)
226
227 def unregister():
228 bpy.utils.unregister_module(__name__)
229 bpy.types.INFO_MT_file_import.remove(menu_func)
230
231
232 if __name__ == "__main__":
233 register()