iPLEOMAX

[Blender 2.69+ Addon] LWO Exporter | io_export_scene_lwo.py

May 11th, 2014
264
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # LightWave Object File exporter for Blender 2.69+
  2. # Copyright (C) 2014  Morshidul Chowdhury
  3.  
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU 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.
  8.  
  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 General Public License for more details.
  13.  
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16.  
  17. bl_info = {
  18.     "name": "Export LightWave Objects",
  19.     "author": "Morshidul Chowdhury (iPLEOMAX)",
  20.     "version": (1, 0),
  21.     "blender": (2, 6, 9),
  22.     "location": "File > Export > LightWave Object (.lwo)",
  23.     "description": "Exports selected objects to LightWave (.lwo) format",
  24.     "warning": "",
  25.     "category": "Import-Export"
  26. }
  27.  
  28. # http://pleo.code5gaming.com/
  29. # http://blenderartists.org/forum/member.php?220524-iPLEOMAX
  30.  
  31. # This is a complete remake of Anthony D'Agostino's LightWave plugin.
  32. #
  33. # Features in R1 (current):
  34. # - Creates an extra '.lwo' option in export menu.
  35. # - Links all image paths to the lightwave file.
  36. # - Supports multi-surfaces (although there are some incompatibility issues)
  37. # - Supports multi-meshes.
  38. # - Supports multi-uv layers.
  39. # - Supports multi-vertex colors.
  40.  
  41. import bpy
  42. import io
  43. import struct
  44. import time
  45. import operator
  46. from functools import reduce
  47.  
  48. LWO_VCOLOR_MATERIAL = "LWO.VColorMaterial"
  49. LWO_DEFAULT_MATERIAL = "LWO.DefaultMaterial"
  50. LWO_ACTIVE_UV = "ActiveUVMap"
  51. LWO_ACTIVE_VC = "ActiveVColMap"
  52.  
  53. # Function from Scorpius's Plugin (Terminates the string and keeps the length even)
  54. def lwopad(str):
  55.     if len(str) % 2: str += '\0' #odd
  56.     else: str += '\0\0' #even
  57.     return str
  58.  
  59. # Function from Scorpius's Plugin (Decides whether to occupy a 4byte or 2byte space in file)
  60. def generate_vx(index):
  61.     if index < 0xFF00:
  62.         value = struct.pack(">H", index)                 # 2-byte index
  63.     else:
  64.         value = struct.pack(">L", index | 0xFF000000)    # 4-byte index
  65.     return value
  66.    
  67. def get_materials(meshes):
  68.     print("\t- Collecting materials..")
  69.     materials = []
  70.     for mesh in meshes:
  71.         if mesh.materials:
  72.             for mat in mesh.materials:
  73.                 if mat:
  74.                     if not mat.name in materials:
  75.                         materials.append(mat.name)
  76.         elif mesh.vertex_colors:
  77.             if not LWO_VCOLOR_MATERIAL in materials:
  78.                 materials.append(LWO_VCOLOR_MATERIAL)
  79.         else:
  80.             if not LWO_DEFAULT_MATERIAL in materials:
  81.                 materials.append(LWO_DEFAULT_MATERIAL)
  82.     return materials
  83.    
  84. def get_tags(materials):
  85.     print("\t- Generating tags..")
  86.     data = io.BytesIO()
  87.     for mat in materials:
  88.         data.write(bytes(lwopad(mat), 'UTF-8'))
  89.     return data.getvalue()
  90.  
  91. def get_layr(mesh, index):
  92.     print("\t- Generating layer for mesh '" + mesh.name + "'")
  93.     data = io.BytesIO()
  94.     data.write(struct.pack(">h", index))
  95.     data.write(struct.pack(">h", 0))
  96.     data.write(struct.pack(">fff", 0, 0, 0))
  97.     data.write(bytes(lwopad(mesh.name), 'UTF-8'))
  98.     return data.getvalue()
  99.    
  100. def get_pnts(mesh):
  101.     print("\t- Generating vertices for mesh '" + mesh.name + "'")
  102.     data = io.BytesIO()
  103.     for v in mesh.vertices:
  104.         x, y, z = v.co
  105.         data.write(struct.pack(">fff", x, z, y))
  106.     return data.getvalue()
  107.    
  108. def get_bbox(mesh):
  109.     print("\t- Generating bounding box for mesh '" + mesh.name + "'")
  110.     data = io.BytesIO()
  111.     if mesh.vertices:
  112.         nv = [v.co for v in mesh.vertices]
  113.         xx = [ co[0] for co in nv ]
  114.         yy = [ co[1] for co in nv ]
  115.         zz = [ co[2] for co in nv ]
  116.     else:
  117.         xx = yy = zz = [0.0,]
  118.    
  119.     data.write(struct.pack(">6f", min(xx), min(zz), min(yy), max(xx), max(zz), max(yy)))
  120.     return data.getvalue()
  121.    
  122. def get_vmap_uv(mesh, uvlayer, activelayername):
  123.     print("\t\tUV Layer: '" + uvlayer.name + "'")
  124.     data = io.BytesIO()
  125.     data.write(b"TXUV")
  126.     data.write(struct.pack(">H", 2))
  127.    
  128.     if uvlayer.name == activelayername:
  129.         uvname = lwopad(LWO_ACTIVE_UV)
  130.         data.write(bytes(uvname, 'UTF-8'))
  131.     else:
  132.         uvname = lwopad(uvlayer.name)
  133.         data.write(bytes(uvname, 'UTF-8'))
  134.        
  135.     for face in mesh.polygons:
  136.         for vert, loop in zip(face.vertices, face.loop_indices):
  137.             uv = uvlayer.data[loop].uv
  138.             data.write(struct.pack(">H", vert))
  139.             data.write(struct.pack(">H", face.index))
  140.             data.write(struct.pack(">ff", uv.x, uv.y))
  141.     return data.getvalue()
  142.  
  143. def get_vmap_vc(mesh, vclayer, activevcname):
  144.     print("\t\tVCOL Layer: '" + vclayer.name + "'")
  145.     data = io.BytesIO()
  146.     data.write(b"RGB ")
  147.     data.write(struct.pack(">H", 3))
  148.    
  149.     if vclayer.name == activevcname:
  150.         vcname = lwopad(LWO_ACTIVE_VC)
  151.         data.write(bytes(vcname, 'UTF-8'))
  152.     else:
  153.         vcname = lwopad(vclayer.name)
  154.         data.write(bytes(vcname, 'UTF-8'))
  155.        
  156.     for face in mesh.polygons:
  157.         for vert, loop in zip(face.vertices, face.loop_indices):
  158.             color =  vclayer.data[loop].color
  159.             data.write(struct.pack(">H", vert))
  160.             data.write(struct.pack(">H", face.index))
  161.             data.write(struct.pack(">fff", color.r, color.g, color.b))
  162.     return data.getvalue()
  163.    
  164. def get_pols(mesh):
  165.     print("\t- Generating polygons for mesh '" + mesh.name + "'")
  166.     data = io.BytesIO()
  167.     data.write(b"FACE")
  168.     for face in mesh.polygons:
  169.         data.write(struct.pack(">H", face.loop_total))
  170.         for v in range(face.loop_total - 1, -1, -1):
  171.             data.write(generate_vx(face.vertices[v]))
  172.     return data.getvalue()
  173.    
  174. def get_ptag(mesh, materials):
  175.     print("\t- Generating per poly materials for mesh '" + mesh.name + "'")
  176.     data = io.BytesIO()
  177.     data.write(b"SURF")
  178.     for poly in mesh.polygons:
  179.         if mesh.materials:
  180.             matindex = poly.material_index
  181.             matname = mesh.materials[matindex].name
  182.             surfindex = materials.index(matname)
  183.            
  184.             data.write(generate_vx(poly.index))
  185.             data.write(struct.pack(">H", surfindex))
  186.         else:
  187.             data.write(generate_vx(poly.index))
  188.             data.write(struct.pack(">H", 0))
  189.     return data.getvalue()
  190.    
  191. def get_clip(image, clipid):
  192.     print("\t\tImage: '" + image.name + "'")
  193.     data = io.BytesIO()
  194.     path = image.filepath
  195.     path = path[0:2] + path.replace("\\", "/")[3:]
  196.     image = lwopad(path)
  197.     data.write(struct.pack(">L", clipid))
  198.     data.write(b"STIL")
  199.     data.write(struct.pack(">H", len(image)))
  200.     data.write(bytes(image, 'UTF-8'))
  201.     return data.getvalue()
  202.  
  203. def get_imap():
  204.     data = io.BytesIO()
  205.     data.write(struct.pack(">H", 0))
  206.     data.write(b"CHAN")
  207.     data.write(struct.pack(">H", 4))
  208.     data.write(b"COLR")
  209.     data.write(b"OPAC")
  210.     data.write(struct.pack(">H", 8))
  211.     data.write(struct.pack(">H", 0))
  212.     data.write(struct.pack(">f", 1.0))
  213.     data.write(struct.pack(">H", 0))
  214.     data.write(b"ENAB")
  215.     data.write(struct.pack(">HH", 2, 1))
  216.     data.write(b"NEGA")
  217.     data.write(struct.pack(">HH", 2, 0))
  218.     data.write(b"AXIS")
  219.     data.write(struct.pack(">HH", 2, 1))
  220.     return data.getvalue()
  221.  
  222. def create_custom_surf(mat):
  223.     print("\t\tCustom Surface: '" + mat.name + "'")
  224.     data = io.BytesIO()
  225.     data.write(bytes(lwopad(mat.name), 'UTF-8'))
  226.    
  227.     R = 0.9
  228.     G = 0.9
  229.     B = 0.9
  230.    
  231.     data.write(b"COLR")
  232.     data.write(struct.pack(">H", 0))
  233.    
  234.     data.write(b"COLR")
  235.     data.write(struct.pack(">H", 14))
  236.     data.write(struct.pack(">fffH", R, G, B, 0))
  237.  
  238.     data.write(b"DIFF")
  239.     data.write(struct.pack(">H", 6))
  240.     data.write(struct.pack(">fH", 0.8, 0))
  241.  
  242.     data.write(b"LUMI")
  243.     data.write(struct.pack(">H", 6))
  244.     data.write(struct.pack(">fH", 0.0, 0))
  245.  
  246.     data.write(b"SPEC")
  247.     data.write(struct.pack(">H", 6))
  248.     data.write(struct.pack(">fH", 0.5, 0))
  249.  
  250.     data.write(b"GLOS")
  251.     data.write(struct.pack(">H", 6))
  252.     gloss = 50 / (255/2.0)
  253.     gloss = round(gloss, 1)
  254.     data.write(struct.pack(">fH", gloss, 0))
  255.    
  256.     if mat and mat.node_tree and mat.node_tree.nodes:
  257.         for node in mat.node_tree.nodes:
  258.             if node:
  259.                 if node.type == 'TEX_IMAGE':
  260.                     data.write(b"BLOK")
  261.                     data.write(struct.pack(">H", 104))
  262.                     imap = get_imap()
  263.                     data.write(b"IMAP")
  264.                     data.write(struct.pack(">H", len(imap)))
  265.                     data.write(imap)
  266.                    
  267.                     data.write(b"IMAG")
  268.                     data.write(struct.pack(">HH", 2, 1))
  269.                     data.write(b"PROJ")
  270.                     data.write(struct.pack(">HH", 2, 5))
  271.                     data.write(b"VMAP")
  272.                     uvname = lwopad(LWO_ACTIVE_UV)
  273.                     data.write(struct.pack(">H", len(uvname)))
  274.                     data.write(bytes(uvname, 'UTF-8'))
  275.                            
  276.     return data.getvalue()
  277.    
  278. def create_surf(matname):
  279.     print("\t\tDefault Surface: '" + mat.name + "'")
  280.     data = io.BytesIO()
  281.     data.write(bytes(lwopad(matname), 'UTF-8'))
  282.    
  283.     R = 0.9
  284.     G = 0.9
  285.     B = 0.9
  286.    
  287.     data.write(b"COLR")
  288.     data.write(struct.pack(">H", 0))
  289.    
  290.     data.write(b"COLR")
  291.     data.write(struct.pack(">H", 14))
  292.     data.write(struct.pack(">fffH", R, G, B, 0))
  293.  
  294.     data.write(b"DIFF")
  295.     data.write(struct.pack(">H", 6))
  296.     data.write(struct.pack(">fH", 0.8, 0))
  297.  
  298.     data.write(b"LUMI")
  299.     data.write(struct.pack(">H", 6))
  300.     data.write(struct.pack(">fH", 0, 0))
  301.  
  302.     data.write(b"SPEC")
  303.     data.write(struct.pack(">H", 6))
  304.     data.write(struct.pack(">fH", 0.5, 0))
  305.  
  306.     data.write(b"GLOS")
  307.     data.write(struct.pack(">H", 6))
  308.     gloss = 50 / (255/2.0)
  309.     gloss = round(gloss, 1)
  310.     data.write(struct.pack(">fH", gloss, 0))
  311.    
  312.     return data.getvalue()
  313.    
  314. def get_surf(matname):
  315.     matid = bpy.data.materials.find(matname)
  316.     if matid != -1:
  317.         return create_custom_surf(bpy.data.materials[matid])
  318.     return create_surf(matname)
  319.    
  320. def get_header(chunks):
  321.     print("\t\tGenerating headers..")
  322.     data = io.BytesIO()
  323.     chunk_sizes = map(len, chunks)
  324.     chunk_sizes = reduce(operator.add, chunk_sizes)
  325.     form_size = chunk_sizes + len(chunks)*8 + len("FORM")
  326.     data.write(b"FORM")
  327.     data.write(struct.pack(">L", form_size))
  328.     data.write(b"LWO2")
  329.     return data.getvalue()
  330.    
  331. def write_lwo(filepath):
  332.    
  333.     print("LWO exporter started..")
  334.     start = time.time()
  335.    
  336.     if not bpy.context.selected_objects:
  337.         print("Please select an object before exporting.")
  338.         return
  339.    
  340.     meshes = []
  341.    
  342.     print("\t- Collecting meshes..")
  343.     for obj in bpy.context.selected_objects:
  344.         if obj.type == 'MESH':
  345.             mesh = obj.to_mesh(bpy.context.scene, True, 'PREVIEW')
  346.             meshes.append(mesh)
  347.             print("\t\tMesh: " + mesh.name)
  348.     del obj
  349.  
  350.     if not meshes:
  351.         print("\tThere are no meshes in your selection.")
  352.         return
  353.    
  354.     materials = get_materials(meshes)
  355.     tags = get_tags(materials)
  356.    
  357.     chunks = [tags]
  358.     meshdata = io.BytesIO()
  359.     meshindex = 0
  360.    
  361.     for mesh in meshes:
  362.         layr = get_layr(mesh, meshindex)
  363.         meshindex += 1
  364.         meshdata.write(b"LAYR")
  365.         meshdata.write(struct.pack(">L", len(layr)))
  366.         meshdata.write(layr)
  367.         chunks.append(layr)
  368.        
  369.         pnts = get_pnts(mesh)
  370.         meshdata.write(b"PNTS")
  371.         meshdata.write(struct.pack(">L", len(pnts)))
  372.         meshdata.write(pnts)
  373.         chunks.append(pnts)
  374.        
  375.         bbox = get_bbox(mesh)
  376.         meshdata.write(b"BBOX")
  377.         meshdata.write(struct.pack(">L", len(bbox)))
  378.         meshdata.write(bbox)
  379.         chunks.append(bbox)
  380.        
  381.         pols = get_pols(mesh)
  382.         meshdata.write(b"POLS")
  383.         meshdata.write(struct.pack(">L", len(pols)))
  384.         meshdata.write(pols)
  385.         chunks.append(pols)
  386.        
  387.         ptag = get_ptag(mesh, materials)
  388.         meshdata.write(b"PTAG")
  389.         meshdata.write(struct.pack(">L", len(ptag)))
  390.         meshdata.write(ptag)
  391.         chunks.append(ptag)
  392.  
  393.         if mesh.uv_layers:
  394.             print("\t- Generating UV layers for mesh '" + mesh.name + "'")
  395.             for uvlayer in mesh.uv_layers:
  396.                 vmap = get_vmap_uv(mesh, uvlayer, mesh.uv_layers.active.name)
  397.                 meshdata.write(b"VMAD")
  398.                 meshdata.write(struct.pack(">L", len(vmap)))
  399.                 meshdata.write(vmap)
  400.                 chunks.append(vmap)
  401.        
  402.         clipid = 1
  403.        
  404.         print("\t- Generating Images for mesh '" + mesh.name + "'")
  405.         for mat in mesh.materials:
  406.             if mat and mat.node_tree and mat.node_tree.nodes:
  407.                 for node in mat.node_tree.nodes:
  408.                     if node:
  409.                         if node.type == 'TEX_IMAGE':
  410.                             clip = get_clip(node.image, clipid)
  411.                             meshdata.write(b"CLIP")
  412.                             meshdata.write(struct.pack(">L", len(clip)))
  413.                             meshdata.write(clip)
  414.                             chunks.append(clip)
  415.                             clipid += 1
  416.        
  417.         if mesh.vertex_colors:
  418.             print("\t- Generating Vertex colors for mesh '" + mesh.name + "'")
  419.             for vclayer in mesh.vertex_colors:
  420.                 vmap = get_vmap_vc(mesh, vclayer, mesh.vertex_colors.active.name)
  421.                 meshdata.write(b"VMAD")
  422.                 meshdata.write(struct.pack(">L", len(vmap)))
  423.                 meshdata.write(vmap)
  424.                 chunks.append(vmap)
  425.        
  426.         bpy.data.meshes.remove(mesh)
  427.    
  428.     print("\t- Generating Surfaces")
  429.     for mat in materials:
  430.         surf = get_surf(mat)
  431.         meshdata.write(b"SURF")
  432.         meshdata.write(struct.pack(">L", len(surf)))
  433.         meshdata.write(surf)
  434.         chunks.append(surf)
  435.    
  436.     header = get_header(chunks)
  437.    
  438.     if not filepath.lower().endswith('.lwo'): filepath += '.lwo'
  439.     file = open(filepath, 'wb')
  440.    
  441.     file.write(header)
  442.    
  443.     file.write(b"TAGS")
  444.     file.write(struct.pack(">L", len(tags)))
  445.     file.write(tags)
  446.    
  447.     file.write(meshdata.getvalue())
  448.     meshdata.close()
  449.    
  450.     file.close()
  451.    
  452.     print("LWO exporter ended in %.03f seconds!" % (time.time() - start))
  453.    
  454. from bpy_extras.io_utils import ExportHelper
  455. from bpy.props import StringProperty, BoolProperty, EnumProperty
  456.  
  457. class EXPORT_OT_lwo(bpy.types.Operator):
  458.  
  459.     bl_idname= "export_scene.lwo"
  460.     bl_label= "Export LightWave"
  461.     bl_description= "Export a LightWave Object file"
  462.     bl_options= {'REGISTER', 'UNDO'}
  463.  
  464.     filepath= StringProperty(name="File Path", description="File name for exporting the LWO file", maxlen=1024, default="")
  465.  
  466.     def execute(self, context):
  467.         write_lwo(self.filepath)
  468.         return {'FINISHED'}
  469.  
  470.     def invoke(self, context, event):
  471.         wm = context.window_manager
  472.         wm.fileselect_add(self)
  473.         return {'RUNNING_MODAL'}
  474.  
  475.  
  476. def menu_func(self, context):
  477.     self.layout.operator(EXPORT_OT_lwo.bl_idname, text="LightWave Object (.lwo)")
  478.  
  479.  
  480. def register():
  481.     bpy.utils.register_module(__name__)
  482.     bpy.types.INFO_MT_file_export.append(menu_func)
  483.  
  484.  
  485. def unregister():
  486.     bpy.utils.unregister_module(__name__)
  487.     bpy.types.INFO_MT_file_export.remove(menu_func)
  488.  
  489. if __name__ == "__main__":
  490.     register()
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×