#!BPY

"""
Name: 'Stl Batch (.stl)...'
Blender: 239
Group: 'Import'
Tooltip: 'Import Stereo Lithography (.stl) File Format'
"""

__author__ = "Mitch Hughes (lobo_nz)"
__url__ = ("blender", "Author's homepage, http://blender.formworks.co.nz")
__version__ = "0.6"

__bpydoc__ = """\
This script batch imports binary Stereo Lithography files into Blender.
OR imports a single stl file

Usage:
    
Add the script to your blender/scripts directory
Execute this script from the "File->Import" menu and choose STL Batch file,
select a file in the directory you would like to batch import .stl files from
OR select a single .stl file and toggle the single file option.
"""

# $Id: batch_stl.py,v 0.6 2005/11/03 20:03:10$
#
# --------------------------------------------------------------------------
# Stl Batch by Mitch Hughes (AKA lobo_nz)
# --------------------------------------------------------------------------
# ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

import Blender, meshtools, os, struct, sys, string
from struct import *
from types import *

from Blender import NMesh, Scene, Object, Draw, Image, BGL
from Blender.Window import *

#Globals

#Detect OS
if (os.name == 'nt'):
    g_os_separator = '\\'
else:
    g_os_separator = '/'
    
g_working_dir=Draw.Create(Blender.sys.dirname(Blender.Get('filename'))+g_os_separator);

g_file_name=Draw.Create("")
    
#Defaults
g_scale=Draw.Create(0.01)
g_set_smooth=Draw.Create(1)
g_auto_smooth=Draw.Create(1)
g_single_file=Draw.Create(0)

# Events
EVENT_NOEVENT=1
EVENT_LOAD_STL=2
EVENT_CHOOSE_FILENAME=3
EVENT_TOGGLE_SETSMOOTH=4
EVENT_TOGGLE_AUTOSMOOTH=5
EVENT_TOGGLE_SINGLEFILE=6
EVENT_EXIT=100

######################################################
# Callbacks for Window functions
######################################################
def filename_callback(filename_in_dir):
    global g_working_dir
    global g_file_name
    g_file_name = Blender.sys.basename(filename_in_dir)
    # Get the target directory to batch import
    g_working_dir.val=Blender.sys.dirname(filename_in_dir)
    
######################################################
# GUI Loader
# GUI Code taken from Bob Holcomb's ASCII stl_import script and modified
######################################################
def draw_gui():
    global g_scale
    global g_working_dir
    global g_scale_slider
    global g_set_smooth
    global g_auto_smooth
    global EVENT_NOEVENT,EVENT_LOAD_STL,EVENT_CHOOSE_FILENAME,EVENT_TOGGLE_SETSMOOTH,EVENT_TOGGLE_AUTOSMOOTH,EVENT_EXIT, EVENT_TOGGLE_SINGLEFILE

    ########## Titles
    BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
    BGL.glRasterPos2d(8, 240)
    Draw.Text("            Binary STL Batch Importer         ")
    BGL.glRasterPos2d(8, 220)
    Draw.Text("1) Select a file from the directory you")
    BGL.glRasterPos2d(8, 205)
    Draw.Text("    wish to Batch Import files from")
    
    ######### Parameters GUI Buttons
    g_working_dir = Draw.String("", EVENT_NOEVENT, 10, 180, 250, 18,
                            g_working_dir.val, 255, "Directory to Batch Import")
    ########## stl File Select Button
    Draw.Button("Select",EVENT_CHOOSE_FILENAME,95,160,80,18)
    
    BGL.glRasterPos2d(8, 135)
    Draw.Text("2) Set the desired Scaling Factor")
    
    ########## Scale slider-default is 0.1
    g_scale= Draw.Slider("Scale Factor: ", EVENT_NOEVENT, 10, 110, 250, 18,
                    g_scale.val, 0.001, 10.0, 1, "Scale factor for STL Models");
    
    BGL.glRasterPos2d(8, 85)
    Draw.Text("3) Set Options")
    Draw.Toggle("Set Smooth", EVENT_TOGGLE_SETSMOOTH, 10, 60, 75, 18, g_set_smooth.val,"Smooths all faces on imported meshes")
    Draw.Toggle("Auto Smooth", EVENT_TOGGLE_AUTOSMOOTH, 98, 60, 75, 18, g_auto_smooth.val,"Activates Auto Smooth on imported meshes")
    Draw.Toggle("Single File", EVENT_TOGGLE_SINGLEFILE, 185, 60, 75, 18, g_single_file.val,"Imports single file rather than whole directory")
                   
    BGL.glRasterPos2d(8, 35)
    Draw.Text("4) Press Import")

    ######### Draw and Exit Buttons
    Draw.Button("Import",EVENT_LOAD_STL , 10, 10, 80, 18)
    Draw.Button("Exit",EVENT_EXIT , 180, 10, 80, 18)

def event(evt, val):
    if (evt == Draw.QKEY and not val):
        Draw.Exit()

def bevent(evt):
    global g_working_dir
    global g_material_filename
    global g_set_smooth
    global g_auto_smooth
    global g_file_name
    global EVENT_NOEVENT,EVENT_LOAD_STL,EVENT_CHOOSE_FILENAME,EVENT_TOGGLE_SETSMOOTH,EVENT_TOGGLE_AUTOSMOOTH,EVENT_EXIT, EVENT_TOGGLE_SINGLEFILE

    ######### Manages GUI events
    if (evt==EVENT_EXIT):
        Draw.Exit()
        return
    elif (evt==EVENT_CHOOSE_FILENAME):
        FileSelector(filename_callback, "Select Directory")
    elif (evt==EVENT_TOGGLE_SETSMOOTH):
        g_set_smooth.val = 1 - g_set_smooth.val
    elif (evt==EVENT_TOGGLE_AUTOSMOOTH):
        g_auto_smooth.val = 1 - g_auto_smooth.val
    elif (evt==EVENT_TOGGLE_SINGLEFILE):
        g_single_file.val = 1 - g_single_file.val
    #load the object and materials
    elif (evt==EVENT_LOAD_STL):
        if (g_working_dir.val == "/path/to/import/from"):
            Draw.Exit()
            return
        else:
            if  (g_single_file.val):
                if string.rfind(string.lower(g_file_name), '.stl')>0:
                    read_file(g_working_dir.val+g_os_separator+g_file_name)
                else:
                    print "Omitting "+file_name
            else:
                iterate_directory(g_working_dir.val)
            Blender.Redraw()
            Draw.Exit()
            return
    Draw.Redraw(1)

######################################################
# Read STL Triangle Format
######################################################

def read_file(filename):
    global g_scale
    global g_set_smooth
    global g_auto_smooth
    
    file = open(filename, "rb")
    
    #80 Any text such as the    global g_working_dir
    #creator's name
    header = unpack("<80s", file.read(80))
    
    #4 int equal to the number of facets in file
    facets = unpack("i", file.read(4))
    
    #4 vertice data in the rest of the data excapt last 2 bytes
    # Collect vert data from RAW format
    faces = []
    for i in range(0, facets[0]):
        n_x = unpack("f", file.read(4))
        n_y = unpack("f", file.read(4))
        n_z = unpack("f", file.read(4))
        v1_x = unpack("f", file.read(4))
        v1_y = unpack("f", file.read(4))
        v1_z = unpack("f", file.read(4))
        v2_x = unpack("f", file.read(4))
        v2_y = unpack("f", file.read(4))
        v2_z = unpack("f", file.read(4))
        v3_x = unpack("f", file.read(4))
        v3_y = unpack("f", file.read(4))
        v3_z = unpack("f", file.read(4))
        unused = file.read(2)
        
        faces.append([(v1_x[0]*g_scale.val, v1_y[0]*g_scale.val, v1_z[0]*g_scale.val), (v2_x[0]*g_scale.val, v2_y[0]*g_scale.val, v2_z[0]*g_scale.val), (v3_x[0]*g_scale.val, v3_y[0]*g_scale.val, v3_z[0]*g_scale.val)])
    file.close()
    
    # The fine piece of code below for eliminating duplicate verts
    # and creating the object in blender was copied directly from
    # the slp import export script written by
    # Anthony D'Agostino (Scorpius)
    # Generate verts and faces lists, without duplicates
    verts = []
    coords = {}
    index = 0
    for i in range(len(faces)):
        for j in range(len(faces[i])):
            vertex = faces[i][j]
            if not coords.has_key(vertex):
                coords[vertex] = index
                index += 1
                verts.append(vertex)
            faces[i][j] = coords[vertex]

    objname = Blender.sys.splitext(Blender.sys.basename(filename))[0]

    meshtools.create_mesh(verts, faces, objname)
    Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
    
    #make all the objects faces smooth
    # Grab selected objects which will be the one we just imported
    selected_ob = Blender.Object.GetSelected()
    # Loop through selected objects and apply methods to meshes only
    for ob in selected_ob:
        if ob.getType() == 'Mesh':
           the_mesh = ob.getData()
           if g_auto_smooth.val == 1:
               the_mesh.setMode("AutoSmooth")
           if g_set_smooth.val == 1:
               setSmooth(the_mesh)
           
           
        
    print "Successfully imported " + Blender.sys.basename(filename)

# A method to do a 'Set Smooth' on a mesh
def setSmooth(mesh):
	for face in mesh.faces:
		face.smooth = 1
	
	mesh.update(1)
######################################################
# Read Files in Directory
######################################################

def iterate_directory(working_dir):
    #Check for stl extension so we dont try and 
    #import other files in the directory
    #stl_extension = re.compile('\.stl$', re.IGNORECASE)
    
    for file_name in os.listdir (working_dir):
        if string.rfind(string.lower(file_name), '.stl')>0:
            read_file(working_dir+g_os_separator+file_name)
        else:
            print "Omitting "+file_name

    
#def fs_callback(filename_in_dir):
#    iterate_directory(filename_in_dir)
#Blender.Window.FileSelector(fs_callback, "Batch STL")

Draw.Register(draw_gui, event, bevent)

