1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 class ThreeDeeParseError(Exception):
27 3D model parser base class. Derrived classes are used for basic
28 analysis of 3D models, and are not intended to be used for 3D
32 def __init__(self
, fileob
):
34 self
.average
= [0, 0, 0]
35 self
.min = [None, None, None]
36 self
.max = [None, None, None]
37 self
.width
= 0 # x axis
38 self
.depth
= 0 # y axis
39 self
.height
= 0 # z axis
42 if not len(self
.verts
):
43 raise ThreeDeeParseError("Empyt model.")
45 for vector
in self
.verts
:
48 self
.average
[i
] += num
59 self
.average
[i
]/=len(self
.verts
)
61 self
.width
= abs(self
.min[0] - self
.max[0])
62 self
.depth
= abs(self
.min[1] - self
.max[1])
63 self
.height
= abs(self
.min[2] - self
.max[2])
66 def load(self
, fileob
):
67 """Override this method in your subclass."""
71 class ObjModel(ThreeDee
):
73 Parser for textureless wavefront obj files. File format
74 reference: http://en.wikipedia.org/wiki/Wavefront_.obj_file
77 def __vector(self
, line
, expected
=3):
78 nums
= map(float, line
.strip().split(" ")[1:])
79 return tuple(nums
[:expected
])
81 def load(self
, fileob
):
84 self
.verts
.append(self
.__vector
(line
))
87 class BinaryStlModel(ThreeDee
):
89 Parser for ascii-encoded stl files. File format reference:
90 http://en.wikipedia.org/wiki/STL_%28file_format%29#Binary_STL
93 def __num(self
, fileob
, hint
):
94 assert hint
== "uint" or hint
== "real" or hint
== "short"
98 form
= "<I" # little-endian unsigned int
101 form
= "<i" # little-endian signed int
103 elif hint
== "short":
104 form
= "<H" # little-endian unsigned short
106 return struct
.unpack(form
, fileob
.read(bits
/8))[0]
108 def __vector(self
, fileob
):
109 return tuple([self
.__num
(fileob
, "real") for n
in range(3)])
111 def load(self
, fileob
):
112 fileob
.seek(80) # skip the header
113 triangle_count
= self
.__num
(fileob
, "uint")
114 for i
in range(triangle_count
):
115 self
.__vector
(fileob
) # skip the normal vector
117 # - FIXME - traingle_count IS reporting the correct
118 # number, but the vertex information appears to be
120 self
.verts
.append(self
.__vector
(fileob
))
121 self
.__num
(fileob
, "short") # skip the attribute byte count
124 def auto_detect(fileob
, hint
):
126 Attempt to divine which parser to use to divine information about
127 the model / verify the file."""
129 if hint
== "obj" or not hint
:
131 return ObjModel(fileob
)
132 except ThreeDeeParseError
:
135 if hint
== "stl" or not hint
:
137 # HACK Ascii formatted stls are similar enough to obj
138 # files that we can just use the same parser for both.
139 # Isn't that something?
140 return ObjModel(fileob
)
141 except ThreeDeeParseError
:
146 # It is pretty important that the binary stl model loader
147 # is tried second, because its possible for it to parse
148 # total garbage from plaintext =)
149 return BinaryStlModel(fileob
)
150 except ThreeDeeParseError
:
155 raise ThreeDeeParseError("Could not successfully parse the model :(")