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