From 25a6ba31f41e405a9279c6fd24f9c51d7d2a453c Mon Sep 17 00:00:00 2001 From: Campbell Barton <ideasman42@gmail.com> Date: Mon, 11 Mar 2013 11:14:35 +0000 Subject: [PATCH] add simple VRML2 exporter, this is mainly intended for use with 3D printers - single mesh export only. currently supports triangles, material/vertex colors. --- io_mesh_vrml2/__init__.py | 123 ++++++++++++++++ io_mesh_vrml2/export_vrml2.py | 260 ++++++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 io_mesh_vrml2/__init__.py create mode 100644 io_mesh_vrml2/export_vrml2.py diff --git a/io_mesh_vrml2/__init__.py b/io_mesh_vrml2/__init__.py new file mode 100644 index 000000000..50ac86e62 --- /dev/null +++ b/io_mesh_vrml2/__init__.py @@ -0,0 +1,123 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8-80 compliant> + +bl_info = { + "name": "VRML2 (Virtual Reality Modeling Language)", + "author": "Campbell Barton", + "blender": (2, 66, 0), + "location": "File > Export", + "description": "Exports the active mesh object to VRML2, supporting vertex and material colors", + "warning": "", + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" + "Scripts/Import-Export/VRML2", + "tracker_url": "", + "support": 'OFFICIAL', + "category": "Import-Export"} + +if "bpy" in locals(): + import imp + if "export_vrml2" in locals(): + imp.reload(export_vrml2) + + +import os +import bpy +from bpy.props import CollectionProperty, StringProperty, BoolProperty, EnumProperty +from bpy_extras.io_utils import ExportHelper, path_reference_mode + +class ExportVRML(bpy.types.Operator, ExportHelper): + """Export a single object as a VRML2, """ \ + """colors and texture coordinates""" + bl_idname = "export_mesh.vrml2" + bl_label = "Export VRML2" + + filename_ext = ".wrl" + filter_glob = StringProperty(default="*.wrl", options={'HIDDEN'}) + + use_mesh_modifiers = BoolProperty( + name="Apply Modifiers", + description="Apply Modifiers to the exported mesh", + default=True, + ) + use_color = BoolProperty( + name="Vertex Colors", + description="Export the active vertex color layer", + default=True, + ) + color_type = EnumProperty( + name='Color', + items=( + ('MATERIAL', "Material Color", ""), + ('VERTEX', "Vertex Color", "")), + default='MATERIAL', + ) + use_uv = BoolProperty( + name="Texture/UVs", + description="Export the active texture and UV coords", + default=True, + ) + + path_mode = path_reference_mode + + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj is not None) and obj.type == 'MESH' + + def execute(self, context): + filepath = self.filepath + filepath = bpy.path.ensure_ext(filepath, self.filename_ext) + from . import export_vrml2 + keywords = self.as_keywords(ignore=("check_existing", "filter_glob")) + return export_vrml2.save(self, context, **keywords) + + def draw(self, context): + layout = self.layout + + row = layout.row() + row.prop(self, "use_mesh_modifiers") + row = layout.row() + row.prop(self, "use_uv") + row.prop(self, "use_color") + row = layout.row() + row.active = self.use_color + row.prop(self, "color_type") + row = layout.row() + row.prop(self, "path_mode") + + + +def menu_func_export(self, context): + self.layout.operator(ExportVRML.bl_idname, text="VRML2 (.wrl)") + + +def register(): + bpy.utils.register_module(__name__) + + bpy.types.INFO_MT_file_export.append(menu_func_export) + + +def unregister(): + bpy.utils.unregister_module(__name__) + + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__ == "__main__": + register() diff --git a/io_mesh_vrml2/export_vrml2.py b/io_mesh_vrml2/export_vrml2.py new file mode 100644 index 000000000..0cea99695 --- /dev/null +++ b/io_mesh_vrml2/export_vrml2.py @@ -0,0 +1,260 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8-80 compliant> + +import bpy +import bpy_extras +import bmesh +import os + +def save_bmesh(fw, bm, + use_color, color_type, material_colors, + use_uv, uv_image, + path_mode, copy_set): + + base_src = os.path.dirname(bpy.data.filepath) + base_dst = os.path.dirname(fw.__self__.name) + + fw('Shape {\n') + fw('\tappearance Appearance {\n') + if use_uv: + fw('\t\ttexture ImageTexture {\n') + filepath = uv_image.filepath + filepath_full = bpy.path.abspath(filepath, library=uv_image.library) + filepath_ref = bpy_extras.io_utils.path_reference(filepath_full, base_src, base_dst, path_mode, "textures", copy_set, uv_image.library) + filepath_base = os.path.basename(filepath_full) + + images = [ + filepath_ref, + filepath_base, + filepath_full, + ] + fw('\t\t\turl [ %s ]\n' % " ".join(['"%s"' % f for f in images]) ) + del images + del filepath_ref, filepath_base, filepath_full, filepath + fw('\t\t}\n') # end 'ImageTexture' + else: + fw('\t\tmaterial Material {\n') + fw('\t\t}\n') # end 'Material' + fw('\t}\n') # end 'Appearance' + + fw('\tgeometry IndexedFaceSet {\n') + fw('\t\tcoord Coordinate {\n') + fw('\t\t\tpoint [ ') + v = None + for v in bm.verts: + fw("%.6f %.6f %.6f " % v.co[:]) + del v + fw(']\n') # end 'point[]' + fw('\t\t}\n') # end 'Coordinate' + + if use_color: + if color_type == 'MATERIAL': + fw('\t\tcolorPerVertex FALSE\n') + fw('\t\tcolor Color {\n') + fw('\t\t\tcolor [ ') + c = None + for c in material_colors: + fw(c) + del c + fw(']\n') # end 'color[]' + fw('\t\t}\n') # end 'Color' + elif color_type == 'VERTEX': + fw('\t\tcolorPerVertex TRUE\n') + fw('\t\tcolor Color {\n') + fw('\t\t\tcolor [ ') + v = None + c_none = "0.00 0.00 0.00 " + color_layer = bm.loops.layers.color.active + assert(color_layer is not None) + for v in bm.verts: + # weak, use first loops color + try: + l = v.link_loops[0] + except: + l = None + fw(c_none if l is None else ("%.2f %.2f %.2f " % l[color_layer][:])) + + del v + fw(']\n') # end 'color[]' + fw('\t\t}\n') # end 'Color' + + # --- + + if color_type == 'MATERIAL': + fw('\t\tcolorIndex [ ') + i = None + for f in bm.faces: + i = f.material_index + if i >= len(material_colors): + i = 0 + fw("%d " % i) + del i + fw(']\n') # end 'colorIndex[]' + elif color_type == 'VERTEX': + pass + + if use_uv: + fw('\t\ttexCoord TextureCoordinate {\n') + fw('\t\t\tpoint [ ') + v = None + uv_layer = bm.loops.layers.uv.active + assert(uv_layer is not None) + for f in bm.faces: + for l in f.loops: + fw("%.4f %.4f " % l[uv_layer].uv[:]) + + del f + fw(']\n') # end 'point[]' + fw('\t\t}\n') # end 'TextureCoordinate' + + # --- + + fw('\t\ttexCoordIndex [ ') + i = None + for i in range(0, len(bm.faces) * 3, 3): + fw("%d %d %d -1 " % (i, i + 1, i + 2)) + del i + fw(']\n') # end 'coordIndex[]' + + fw('\t\tcoordIndex [ ') + f = fv = None + for f in bm.faces: + fv = f.verts[:] + fw("%d %d %d -1 " % (fv[0].index, fv[1].index, fv[2].index)) + del f, fv + fw(']\n') # end 'coordIndex[]' + + fw('\t}\n') # end 'IndexedFaceSet' + fw('}\n') # end 'Shape' + + +def detect_default_image(obj, bm): + tex_layer = bm.faces.layers.tex.active + if tex_layer is not None: + for f in bm.faces: + image = f[tex_layer].image + if image is not None: + return image + for m in obj.data.materials: + if m is not None: + # backwards so topmost are highest priority + for mtex in reversed(m.texture_slots): + if mtex and mtex.use_map_color_diffuse: + texture = mtex.texture + if texture and texture.type == 'IMAGE': + image = texture.image + if image is not None: + return image + return None + + +def save_object(fw, scene, obj, + use_mesh_modifiers, + use_color, color_type, + use_uv, + path_mode, copy_set): + + assert(obj.type == 'MESH') + + if use_mesh_modifiers: + is_editmode = (obj.mode == 'EDIT') + if is_editmode: + bpy.ops.object.editmode_toggle() + + me = obj.to_mesh(scene, True, 'PREVIEW', calc_tessface=False) + bm = bmesh.new() + bm.from_mesh(me) + + if is_editmode: + bpy.ops.object.editmode_toggle() + else: + me = obj.data + if obj.mode == 'EDIT': + bm_orig = bmesh.from_edit_mesh(me) + bm = bm_orig.copy() + else: + bm = bmesh.new() + bm.from_mesh(me) + + bm.transform(obj.matrix_world) + bmesh.ops.triangulate(bm, faces=bm.faces, use_beauty=True) + + # default empty + material_colors = [] + uv_image = None + + if use_color: + if color_type == 'VERTEX': + if bm.loops.layers.color.active is None: + use_color = False + elif color_type == 'MATERIAL': + if not me.materials: + use_color = False + else: + material_colors = [ + "%.2f %.2f %.2f " % (m.diffuse_color[:] if m else (1.0, 1.0, 1.0)) + for m in me.materials] + else: + assert(0) + + if use_uv: + if bm.loops.layers.uv.active is None: + use_uv = False + uv_image = detect_default_image(obj, bm) + if uv_image is None: + use_uv = False + + save_bmesh(fw, bm, + use_color, color_type, material_colors, + use_uv, uv_image, + path_mode, copy_set) + + bm.free() + + +def save(operator, + context, + filepath="", + use_mesh_modifiers=True, + use_color=True, + color_type='MATERIAL', + use_uv=True, + path_mode='AUTO'): + + # store files to copy + copy_set = set() + + file = open(filepath, 'w', encoding='utf-8') + fw = file.write + fw('#VRML V2.0 utf8\n') + fw('#modeled using blender3d http://blender.org\n') + + save_object(fw, context.scene, context.object, + use_mesh_modifiers, + use_color, color_type, + use_uv, + path_mode, copy_set) + + file.close() + + # copy all collected files. + bpy_extras.io_utils.path_reference_copy(copy_set) + + return {'FINISHED'} -- GitLab