Bläddra i källkod

Merge pull request #6052 from repsac/io_three

Restored original scene logic and structure
Mr.doob 10 år sedan
förälder
incheckning
ab094339e8

+ 2 - 2
utils/exporters/blender/addons/io_three/__init__.py

@@ -41,9 +41,9 @@ SETTINGS_FILE_EXPORT = 'three_settings_export.js'
 bl_info = {
     'name': "Three.js Format",
     'author': "repsac, mrdoob, yomotsu, mpk, jpweeks",
-    'version': (1, 2, 1),
+    'version': (1, 2, 2),
     'blender': (2, 7, 3),
-    'location': "File > Import-Export",
+    'location': "File > Export",
     'description': "Export Three.js formatted JSON files.",
     'warning': "Importer not included.",
     'wiki_url': "https://github.com/mrdoob/three.js/tree/"\

+ 28 - 4
utils/exporters/blender/addons/io_three/exporter/api/__init__.py

@@ -1,20 +1,33 @@
 import os
 import bpy
-from . import object, mesh, material, camera, light
+from . import object as object_, mesh, material, camera, light
 from .. import logger
 
 
 def active_object():
+    """
+
+    :return: The actively selected object
+
+    """
     return bpy.context.scene.objects.active
 
 
 def init():
-    logger.debug('Initializing API')
-    object._MESH_MAP.clear()
+    """Initializing the api module. Required first step before
+    initializing the actual export process.
+    """
+    logger.debug("Initializing API")
+    object_.clear_mesh_map()
 
 
 def selected_objects(valid_types=None):
-    logger.debug('api.selected_objects(%s)', valid_types)
+    """Selected objects.
+
+    :param valid_types: Filter for valid types (Default value = None)
+
+    """
+    logger.debug("api.selected_objects(%s)", valid_types)
     for node in bpy.context.selected_objects:
         if valid_types is None:
             yield node.name
@@ -23,9 +36,20 @@ def selected_objects(valid_types=None):
 
 
 def set_active_object(obj):
+    """Set the object as active in the scene
+
+    :param obj:
+
+    """
+    logger.debug("api.set_active_object(%s)", obj)
     bpy.context.scene.objects.active = obj
 
 
 def scene_name():
+    """
+
+    :return: name of the current scene
+
+    """
     return os.path.basename(bpy.data.filepath)
 

+ 144 - 49
utils/exporters/blender/addons/io_three/exporter/api/animation.py

@@ -4,34 +4,66 @@ from .. import constants, logger, utilities
 
 
 def pose_animation(armature, options):
-    logger.debug('animation.pose_animation %s', armature)
+    """Query armature animation using pose bones
+
+    :param armature:
+    :param options:
+    :returns: list dictionaries containing animationdata
+    :rtype: [{}, {}, ...]
+
+    """
+    logger.debug("animation.pose_animation(%s)", armature)
     func = _parse_pose_action
     return _parse_action(func, armature, options)
 
 
 def rest_animation(armature, options):
-    logger.debug('animation.rest_animation %s', armature)
+    """Query armature animation (REST position)
+
+    :param armature:
+    :param options:
+    :returns: list dictionaries containing animationdata
+    :rtype: [{}, {}, ...]
+
+    """
+    logger.debug("animation.rest_animation(%s)", armature)
     func = _parse_rest_action
     return _parse_action(func, armature, options)
 
 
 def _parse_action(func, armature, options):
+    """
+
+    :param func:
+    :param armature:
+    :param options:
+
+    """
     animations = []
-    logger.info('Parsing %d actions', len(data.actions))
+    logger.info("Parsing %d actions", len(data.actions))
     round_off, round_val = utilities.rounding(options)
     for action in data.actions:
-        logger.info('Parsing action %s', action.name)
+        logger.info("Parsing action %s", action.name)
         animation = func(action, armature, options, round_off, round_val)
         animations.append(animation)
     return animations
 
 
 def _parse_rest_action(action, armature, options, round_off, round_val):
+    """
+
+    :param action:
+    :param armature:
+    :param options:
+    :param round_off:
+    :param round_val:
+
+    """
     end_frame = action.frame_range[1]
     start_frame = action.frame_range[0]
     frame_length = end_frame - start_frame
-    l,r,s = armature.matrix_world.decompose()
-    rotation_matrix = r.to_matrix()
+    rot = armature.matrix_world.decompose()[1]
+    rotation_matrix = rot.to_matrix()
     hierarchy = []
     parent_index = -1
     frame_step = options.get(constants.FRAME_STEP, 1)
@@ -44,22 +76,27 @@ def _parse_rest_action(action, armature, options, round_off, round_val):
         # I believe this was meant to skip control bones, may
         # not be useful. needs more testing
         if bone.use_deform is False:
-            logger.info('Skipping animation data for bone %s', bone.name)
+            logger.info("Skipping animation data for bone %s", bone.name)
             continue
 
-        logger.info('Parsing animation data for bone %s', bone.name)
+        logger.info("Parsing animation data for bone %s", bone.name)
 
         keys = []
         for frame in range(start, end):
             computed_frame = frame * frame_step
-            pos, pchange = _position(bone, computed_frame, 
-                action, armature.matrix_world)
-            rot, rchange = _rotation(bone, computed_frame, 
-                action, rotation_matrix)
+            pos, pchange = _position(bone, computed_frame,
+                                     action, armature.matrix_world)
+            rot, rchange = _rotation(bone, computed_frame,
+                                     action, rotation_matrix)
 
-            # flip y and z
-            px, py, pz = pos.x, pos.z, -pos.y
-            rx, ry, rz, rw = rot.x, rot.z, -rot.y, rot.w
+            if round_off:
+                pos_x, pos_y, pos_z = utilities.round_off(
+                    [pos.x, pos.z, -pos.y], round_val)
+                rot_x, rot_y, rot_z, rot_w = utilities.round_off(
+                    [rot.x, rot.z, -rot.y, rot.w], round_val)
+            else:
+                pos_x, pos_y, pos_z = pos.x, pos.z, -pos.y
+                rot_x, rot_y, rot_z, rot_w = rot.x, rot.z, -rot.y, rot.w
 
             if frame == start_frame:
 
@@ -67,13 +104,13 @@ def _parse_rest_action(action, armature, options, round_off, round_val):
                 #@TODO: missing scale values
                 keyframe = {
                     constants.TIME: time,
-                    constants.POS: [px, py, pz],
-                    constants.ROT: [rx, ry, rz, rw],
+                    constants.POS: [pos_x, pos_y, pos_z],
+                    constants.ROT: [rot_x, rot_y, rot_z, rot_w],
                     constants.SCL: [1, 1, 1]
                 }
                 keys.append(keyframe)
 
-            # END-FRAME: needs pos, rot and scl attributes 
+            # END-FRAME: needs pos, rot and scl attributes
             # with animation length (required frame)
 
             elif frame == end_frame / frame_step:
@@ -81,13 +118,13 @@ def _parse_rest_action(action, armature, options, round_off, round_val):
                 time = frame_length / fps
                 keyframe = {
                     constants.TIME: time,
-                    constants.POS: [px, py, pz],
-                    constants.ROT: [rx, ry, rz, rw],
+                    constants.POS: [pos_x, pos_y, pos_z],
+                    constants.ROT: [rot_x, rot_y, rot_z, rot_w],
                     constants.SCL: [1, 1, 1]
                 }
                 keys.append(keyframe)
 
-            # MIDDLE-FRAME: needs only one of the attributes, 
+            # MIDDLE-FRAME: needs only one of the attributes,
             # can be an empty frame (optional frame)
 
             elif pchange == True or rchange == True:
@@ -96,31 +133,31 @@ def _parse_rest_action(action, armature, options, round_off, round_val):
 
                 if pchange == True and rchange == True:
                     keyframe = {
-                        constants.TIME: time, 
-                        constants.POS: [px, py, pz],
-                        constants.ROT: [rx, ry, rz, rw]
+                        constants.TIME: time,
+                        constants.POS: [pos_x, pos_y, pos_z],
+                        constants.ROT: [rot_x, rot_y, rot_z, rot_w]
                     }
                 elif pchange == True:
                     keyframe = {
-                        constants.TIME: time, 
-                        constants.POS: [px, py, pz]
+                        constants.TIME: time,
+                        constants.POS: [pos_x, pos_y, pos_z]
                     }
                 elif rchange == True:
                     keyframe = {
-                        constants.TIME: time, 
-                        constants.ROT: [rx, ry, rz, rw]
+                        constants.TIME: time,
+                        constants.ROT: [rot_x, rot_y, rot_z, rot_w]
                     }
 
                 keys.append(keyframe)
 
         hierarchy.append({
-            constants.KEYS: keys, 
+            constants.KEYS: keys,
             constants.PARENT: parent_index
         })
         parent_index += 1
 
     animation = {
-        constants.HIERARCHY: hierarchy, 
+        constants.HIERARCHY: hierarchy,
         constants.LENGTH:frame_length / fps,
         constants.FPS: fps,
         constants.NAME: action.name
@@ -130,6 +167,15 @@ def _parse_rest_action(action, armature, options, round_off, round_val):
 
 
 def _parse_pose_action(action, armature, options, round_off, round_val):
+    """
+
+    :param action:
+    :param armature:
+    :param options:
+    :param round_off:
+    :param round_val:
+
+    """
     #@TODO: this seems to fail in batch mode meaning the
     #       user has to have th GUI open. need to improve
     #       this logic to allow batch processing, if Blender
@@ -138,7 +184,7 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
     context.area.type = 'DOPESHEET_EDITOR'
     context.space_data.mode = 'ACTION'
     context.area.spaces.active.action = action
-    
+
     armature_matrix = armature.matrix_world
     fps = context.scene.render.fps
 
@@ -155,23 +201,23 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
     channels_scale = []
 
     for pose_bone in armature.pose.bones:
-        logger.info('Processing channels for %s', 
+        logger.info("Processing channels for %s",
                     pose_bone.bone.name)
         keys.append([])
         channels_location.append(
-            _find_channels(action, 
+            _find_channels(action,
                            pose_bone.bone,
                            'location'))
         channels_rotation.append(
-            _find_channels(action, 
+            _find_channels(action,
                            pose_bone.bone,
                            'rotation_quaternion'))
         channels_rotation.append(
-            _find_channels(action, 
+            _find_channels(action,
                            pose_bone.bone,
                            'rotation_euler'))
         channels_scale.append(
-            _find_channels(action, 
+            _find_channels(action,
                            pose_bone.bone,
                            'scale'))
 
@@ -183,7 +229,7 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
         else:
             frame = start_frame + frame_index * frame_step
 
-        logger.info('Processing frame %d', frame)
+        logger.info("Processing frame %d", frame)
 
         time = frame - start_frame
         if frame_index_as_time is False:
@@ -194,7 +240,19 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
         bone_index = 0
 
         def has_keyframe_at(channels, frame):
+            """
+
+            :param channels:
+            :param frame:
+
+            """
             def find_keyframe_at(channel, frame):
+                """
+
+                :param channel:
+                :param frame:
+
+                """
                 for keyframe in channel.keyframe_points:
                     if keyframe.co[0] == frame:
                         return keyframe
@@ -207,7 +265,7 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
 
         for pose_bone in armature.pose.bones:
 
-            logger.info('Processing bone %s', pose_bone.bone.name)
+            logger.info("Processing bone %s", pose_bone.bone.name)
             if pose_bone.parent is None:
                 bone_matrix = armature_matrix * pose_bone.matrix
             else:
@@ -216,7 +274,7 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
                 bone_matrix = parent_matrix.inverted() * bone_matrix
 
             pos, rot, scl = bone_matrix.decompose()
-            
+
             pchange = True or has_keyframe_at(
                 channels_location[bone_index], frame)
             rchange = True or has_keyframe_at(
@@ -232,7 +290,7 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
                 )
                 rot = (
                     utilities.round_off(rot.x, round_val),
-                    utilities.round_off(rot.z, round_val), 
+                    utilities.round_off(rot.z, round_val),
                     -utilities.round_off(rot.y, round_val),
                     utilities.round_off(rot.w, round_val)
                 )
@@ -262,15 +320,15 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
                     keyframe[constants.SCL] = scl
 
             if len(keyframe.keys()) > 1:
-                logger.info('Recording keyframe data for %s %s',
+                logger.info("Recording keyframe data for %s %s",
                             pose_bone.bone.name, str(keyframe))
                 keys[bone_index].append(keyframe)
             else:
-                logger.info('No anim data to record for %s',
+                logger.info("No anim data to record for %s",
                             pose_bone.bone.name)
 
             bone_index += 1
-    
+
     hierarchy = []
     bone_index = 0
     for pose_bone in armature.pose.bones:
@@ -285,9 +343,9 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
 
     context.scene.frame_set(start_frame)
     context.area.type = current_context
-    
+
     animation = {
-        constants.HIERARCHY: hierarchy, 
+        constants.HIERARCHY: hierarchy,
         constants.LENGTH:frame_length,
         constants.FPS: fps,
         constants.NAME: action.name
@@ -297,10 +355,17 @@ def _parse_pose_action(action, armature, options, round_off, round_val):
 
 
 def _find_channels(action, bone, channel_type):
+    """
+
+    :param action:
+    :param bone:
+    :param channel_type:
+
+    """
     result = []
 
     if len(action.groups):
-        
+
         group_index = -1
         for index, group in enumerate(action.groups):
             if group.name == bone.name:
@@ -316,7 +381,7 @@ def _find_channels(action, bone, channel_type):
         bone_label = '"%s"' % bone.name
         for channel in action.fcurves:
             data_path = [bone_label in channel.data_path,
-                channel_type in channel.data_path]
+                         channel_type in channel.data_path]
             if all(data_path):
                 result.append(channel)
 
@@ -324,8 +389,16 @@ def _find_channels(action, bone, channel_type):
 
 
 def _position(bone, frame, action, armature_matrix):
+    """
+
+    :param bone:
+    :param frame:
+    :param action:
+    :param armature_matrix:
 
-    position = mathutils.Vector((0,0,0))
+    """
+
+    position = mathutils.Vector((0, 0, 0))
     change = False
 
     ngroups = len(action.groups)
@@ -379,10 +452,18 @@ def _position(bone, frame, action, armature_matrix):
 
 
 def _rotation(bone, frame, action, armature_matrix):
+    """
+
+    :param bone:
+    :param frame:
+    :param action:
+    :param armature_matrix:
+
+    """
 
     # TODO: calculate rotation also from rotation_euler channels
 
-    rotation = mathutils.Vector((0,0,0,1))
+    rotation = mathutils.Vector((0, 0, 0, 1))
 
     change = False
 
@@ -427,6 +508,13 @@ def _rotation(bone, frame, action, armature_matrix):
 
 
 def _handle_rotation_channel(channel, frame, rotation):
+    """
+
+    :param channel:
+    :param frame:
+    :param rotation:
+
+    """
 
     change = False
 
@@ -454,6 +542,13 @@ def _handle_rotation_channel(channel, frame, rotation):
 
 
 def _handle_position_channel(channel, frame, position):
+    """
+
+    :param channel:
+    :param frame:
+    :param position:
+
+    """
 
     change = False
 

+ 69 - 9
utils/exporters/blender/addons/io_three/exporter/api/camera.py

@@ -3,13 +3,25 @@ from .. import logger
 
 
 def _camera(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Camera):
             camera = name
         else:
-            camera = data.cameras[name] 
+            camera = data.cameras[name]
 
         return func(camera, *args, **kwargs)
 
@@ -18,48 +30,96 @@ def _camera(func):
 
 @_camera
 def aspect(camera):
-    logger.debug('camera.aspect(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.aspect(%s)", camera)
     render = context.scene.render
     return render.resolution_x/render.resolution_y
 
 
 @_camera
 def bottom(camera):
-    logger.debug('camera.bottom(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.bottom(%s)", camera)
     return  -(camera.angle_y * camera.ortho_scale)
 
 
 @_camera
 def far(camera):
-    logger.debug('camera.far(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.far(%s)", camera)
     return camera.clip_end
 
 
 @_camera
 def fov(camera):
-    logger.debug('camera.fov(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.fov(%s)", camera)
     return camera.lens
 
 
 @_camera
 def left(camera):
-    logger.debug('camera.left(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.left(%s)", camera)
     return -(camera.angle_x * camera.ortho_scale)
 
 
 @_camera
 def near(camera):
-    logger.debug('camera.near(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.near(%s)", camera)
     return camera.clip_start
 
 
 @_camera
 def right(camera):
-    logger.debug('camera.right(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.right(%s)", camera)
     return camera.angle_x * camera.ortho_scale
 
 
 @_camera
 def top(camera):
-    logger.debug('camera.top(%s)', camera)
+    """
+
+    :param camera:
+    :rtype: float
+
+    """
+    logger.debug("camera.top(%s)", camera)
     return camera.angle_y * camera.ortho_scale

+ 28 - 4
utils/exporters/blender/addons/io_three/exporter/api/image.py

@@ -4,13 +4,25 @@ from .. import logger
 
 
 def _image(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Image):
             mesh = name
         else:
-            mesh = data.images[name] 
+            mesh = data.images[name]
 
         return func(mesh, *args, **kwargs)
 
@@ -18,11 +30,23 @@ def _image(func):
 
 
 def file_name(image):
-    logger.debug('image.file_name(%s)', image)
-    return os.path.basename(file_path(image)) 
+    """
+
+    :param image:
+    :rtype: str
+
+    """
+    logger.debug("image.file_name(%s)", image)
+    return os.path.basename(file_path(image))
 
 
 @_image
 def file_path(image):
-    logger.debug('image.file_path(%s)', image)
+    """
+
+    :param image:
+    :rtype: str
+
+    """
+    logger.debug("image.file_path(%s)", image)
     return os.path.normpath(image.filepath_from_user())

+ 42 - 6
utils/exporters/blender/addons/io_three/exporter/api/light.py

@@ -1,15 +1,27 @@
-from bpy import data, types 
+from bpy import data, types
 from .. import utilities, logger
 
 
 def _lamp(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Lamp):
             lamp = name
         else:
-            lamp = data.lamps[name] 
+            lamp = data.lamps[name]
 
         return func(lamp, *args, **kwargs)
 
@@ -18,24 +30,48 @@ def _lamp(func):
 
 @_lamp
 def angle(lamp):
-    logger.debug('light.angle(%s)', lamp)
+    """
+
+    :param lamp:
+    :rtype: float
+
+    """
+    logger.debug("light.angle(%s)", lamp)
     return lamp.spot_size
 
 
 @_lamp
 def color(lamp):
-    logger.debug('light.color(%s)', lamp)
+    """
+
+    :param lamp:
+    :rtype: int
+
+    """
+    logger.debug("light.color(%s)", lamp)
     colour = (lamp.color.r, lamp.color.g, lamp.color.b)
     return utilities.rgb2int(colour)
 
 
 @_lamp
 def distance(lamp):
-    logger.debug('light.distance(%s)', lamp)
+    """
+
+    :param lamp:
+    :rtype: float
+
+    """
+    logger.debug("light.distance(%s)", lamp)
     return lamp.distance
 
 
 @_lamp
 def intensity(lamp):
-    logger.debug('light.intensity(%s)', lamp)
+    """
+
+    :param lamp:
+    :rtype: float
+
+    """
+    logger.debug("light.intensity(%s)", lamp)
     return round(lamp.energy, 2)

+ 203 - 38
utils/exporters/blender/addons/io_three/exporter/api/material.py

@@ -4,13 +4,25 @@ from .constants import MULTIPLY, WIRE, IMAGE
 
 
 def _material(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Material):
             material = name
         else:
-            material = data.materials[name] 
+            material = data.materials[name]
 
         return func(material, *args, **kwargs)
 
@@ -19,8 +31,15 @@ def _material(func):
 
 @_material
 def ambient_color(material):
-    logger.debug('material.ambient_color(%s)', material)
-    diffuse = diffuse_color(material) 
+    """
+
+    :param material:
+    :return: rgb value
+    :rtype: tuple
+
+    """
+    logger.debug("material.ambient_color(%s)", material)
+    diffuse = diffuse_color(material)
     return (material.ambient * diffuse[0],
             material.ambient * diffuse[1],
             material.ambient * diffuse[2])
@@ -28,18 +47,30 @@ def ambient_color(material):
 
 @_material
 def blending(material):
-    logger.debug('material.blending(%s)', material)
+    """
+
+    :param material:
+    :return: THREE_blending_type value
+
+    """
+    logger.debug("material.blending(%s)", material)
     try:
         blend = material.THREE_blending_type
     except AttributeError:
-        logger.debug('No THREE_blending_type attribute found')
-        blend = constants.NORMAL_BLENDING 
+        logger.debug("No THREE_blending_type attribute found")
+        blend = constants.NORMAL_BLENDING
     return blend
 
 
 @_material
 def bump_map(material):
-    logger.debug('material.bump_map(%s)', material)
+    """
+
+    :param material:
+    :return: texture node for bump
+
+    """
+    logger.debug("material.bump_map(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_normal and not \
         texture.texture.use_normal_map:
@@ -48,34 +79,61 @@ def bump_map(material):
 
 @_material
 def bump_scale(material):
+    """
+
+    :param material:
+    :rtype: float
+
+    """
     return normal_scale(material)
 
 
 @_material
 def depth_test(material):
-    logger.debug('material.depth_test(%s)', material)
+    """
+
+    :param material:
+    :return: THREE_depth_test value
+    :rtype: bool
+
+    """
+    logger.debug("material.depth_test(%s)", material)
     try:
         test = material.THREE_depth_test
     except AttributeError:
-        logger.debug('No THREE_depth_test attribute found')
+        logger.debug("No THREE_depth_test attribute found")
         test = True
     return test
 
 
 @_material
 def depth_write(material):
-    logger.debug('material.depth_write(%s)', material)
+    """
+
+    :param material:
+    :return: THREE_depth_write value
+    :rtype: bool
+
+    """
+    logger.debug("material.depth_write(%s)", material)
     try:
         write = material.THREE_depth_write
     except AttributeError:
-        logger.debug('No THREE_depth_write attribute found')
+        logger.debug("No THREE_depth_write attribute found")
         write = True
     return write
 
 
 @_material
 def diffuse_color(material):
-    logger.debug('material.diffuse_color(%s)', material)
+    """
+
+    :param material:
+    :return: rgb value
+    :rtype: tuple
+
+    """
+    logger.debug("material.diffuse_color(%s)", material)
     return (material.diffuse_intensity * material.diffuse_color[0],
             material.diffuse_intensity * material.diffuse_color[1],
             material.diffuse_intensity * material.diffuse_color[2])
@@ -83,7 +141,13 @@ def diffuse_color(material):
 
 @_material
 def diffuse_map(material):
-    logger.debug('material.diffuse_map(%s)', material)
+    """
+
+    :param material:
+    :return: texture node for map
+
+    """
+    logger.debug("material.diffuse_map(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_color_diffuse and not \
         texture.blend_type == MULTIPLY:
@@ -92,8 +156,15 @@ def diffuse_map(material):
 
 @_material
 def emissive_color(material):
-    logger.debug('material.emissive_color(%s)', material)
-    diffuse = diffuse_color(material) 
+    """
+
+    :param material:
+    :return: rgb value
+    :rtype: tuple
+
+    """
+    logger.debug("material.emissive_color(%s)", material)
+    diffuse = diffuse_color(material)
     return (material.emit * diffuse[0],
             material.emit * diffuse[1],
             material.emit * diffuse[2])
@@ -101,7 +172,13 @@ def emissive_color(material):
 
 @_material
 def light_map(material):
-    logger.debug('material.light_map(%s)', material)
+    """
+
+    :param material:
+    :return: texture node for light maps
+
+    """
+    logger.debug("material.light_map(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_color_diffuse and \
         texture.blend_type == MULTIPLY:
@@ -110,7 +187,13 @@ def light_map(material):
 
 @_material
 def normal_scale(material):
-    logger.debug('material.normal_scale(%s)', material)
+    """
+
+    :param material:
+    :rtype: float
+
+    """
+    logger.debug("material.normal_scale(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_normal:
             return texture.normal_factor
@@ -118,22 +201,40 @@ def normal_scale(material):
 
 @_material
 def normal_map(material):
-    logger.debug('material.normal_map(%s)', material)
+    """
+
+    :param material:
+    :return: texture node for normals
+
+    """
+    logger.debug("material.normal_map(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_normal and \
         texture.texture.use_normal_map:
             return texture.texture
- 
+
 
 @_material
 def opacity(material):
-    logger.debug('material.opacity(%s)', material)
-    return round( material.alpha, 2 );
+    """
+
+    :param material:
+    :rtype: float
+
+    """
+    logger.debug("material.opacity(%s)", material)
+    return round(material.alpha, 2)
 
 
 @_material
 def shading(material):
-    logger.debug('material.shading(%s)', material)
+    """
+
+    :param material:
+    :return: shading type (phong or lambert)
+
+    """
+    logger.debug("material.shading(%s)", material)
     dispatch = {
         True: constants.PHONG,
         False: constants.LAMBERT
@@ -144,21 +245,40 @@ def shading(material):
 
 @_material
 def specular_coef(material):
-    logger.debug('material.specular_coef(%s)', material)
+    """
+
+    :param material:
+    :rtype: float
+
+    """
+    logger.debug("material.specular_coef(%s)", material)
     return material.specular_hardness
- 
+
 
 @_material
 def specular_color(material):
-    logger.debug('material.specular_color(%s)', material)
+    """
+
+    :param material:
+    :return: rgb value
+    :rtype: tuple
+
+    """
+    logger.debug("material.specular_color(%s)", material)
     return (material.specular_intensity * material.specular_color[0],
             material.specular_intensity * material.specular_color[1],
             material.specular_intensity * material.specular_color[2])
-  
+
 
 @_material
 def specular_map(material):
-    logger.debug('material.specular_map(%s)', material)
+    """
+
+    :param material:
+    :return: texture node for specular
+
+    """
+    logger.debug("material.specular_map(%s)", material)
     for texture in _valid_textures(material):
         if texture.use_map_specular:
             return texture.texture
@@ -166,13 +286,25 @@ def specular_map(material):
 
 @_material
 def transparent(material):
-    logger.debug('material.transparent(%s)', material)
+    """
+
+    :param material:
+    :rtype: bool
+
+    """
+    logger.debug("material.transparent(%s)", material)
     return material.use_transparency
 
 
 @_material
 def type(material):
-    logger.debug('material.type(%s)', material)
+    """
+
+    :param material:
+    :return: THREE compatible shader type
+
+    """
+    logger.debug("material.type(%s)", material)
     if material.diffuse_shader != 'LAMBERT':
         material_type = constants.BASIC
     elif material.specular_intensity > 0:
@@ -185,23 +317,42 @@ def type(material):
 
 @_material
 def use_vertex_colors(material):
-    logger.debug('material.use_vertex_colors(%s)', material)
+    """
+
+    :param material:
+    :rtype: bool
+
+    """
+    logger.debug("material.use_vertex_colors(%s)", material)
     return material.use_vertex_color_paint
 
 
 def used_materials():
-    logger.debug('material.used_materials()')
+    """
+
+    :return: list of materials that are in use
+    :rtype: generator
+
+    """
+    logger.debug("material.used_materials()")
     for material in data.materials:
         if material.users > 0:
             yield material.name
 
 @_material
 def visible(material):
-    logger.debug('material.visible(%s)', material)
+    """
+
+    :param material:
+    :return: THREE_visible value
+    :rtype: bool
+
+    """
+    logger.debug("material.visible(%s)", material)
     try:
         vis = material.THREE_visible
     except AttributeError:
-        logger.debug('No THREE_visible attribute found')
+        logger.debug("No THREE_visible attribute found")
         vis = True
 
     return vis
@@ -209,13 +360,27 @@ def visible(material):
 
 @_material
 def wireframe(material):
-    logger.debug('material.wireframe(%s)', material)
+    """
+
+    :param material:
+    :rtype: bool
+
+    """
+    logger.debug("material.wireframe(%s)", material)
     return material.type == WIRE
 
- 
+
 def _valid_textures(material):
+    """
+
+    :param material:
+    :rtype: generator
+
+    """
     for texture in material.texture_slots:
-        if not texture: continue
-        if texture.texture.type != IMAGE: continue
-        logger.debug('Valid texture found %s', texture)
+        if not texture:
+            continue
+        if texture.texture.type != IMAGE:
+            continue
+        logger.debug("Valid texture found %s", texture)
         yield texture

+ 332 - 155
utils/exporters/blender/addons/io_three/exporter/api/mesh.py

@@ -6,13 +6,25 @@ from .. import constants, utilities, logger, exceptions
 
 
 def _mesh(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Mesh):
             mesh = name
         else:
-            mesh = data.meshes[name] 
+            mesh = data.meshes[name]
 
         return func(mesh, *args, **kwargs)
 
@@ -21,11 +33,17 @@ def _mesh(func):
 
 @_mesh
 def skeletal_animation(mesh, options):
-    logger.debug('mesh.animation(%s, %s)', mesh, options)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.animation(%s, %s)", mesh, options)
     armature = _armature(mesh)
 
     if not armature:
-        logger.warning('No armature found (%s)', mesh)
+        logger.warning("No armature found (%s)", mesh)
         return []
 
     anim_type = options.get(constants.ANIMATION)
@@ -34,21 +52,27 @@ def skeletal_animation(mesh, options):
         constants.POSE: animation.pose_animation,
         constants.REST: animation.rest_animation
     }
-    
+
     func = dispatch[anim_type]
     #armature.data.pose_position = anim_type.upper()
     animations = func(armature, options)
     #armature.data.pose_position = pose_position
-        
+
     return animations
 
 
 @_mesh
 def bones(mesh, options):
-    logger.debug('mesh.bones(%s)', mesh)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.bones(%s)", mesh)
     armature = _armature(mesh)
 
-    if not armature: 
+    if not armature:
         return [], {}
 
     round_off, round_val = utilities.rounding(options)
@@ -56,34 +80,40 @@ def bones(mesh, options):
     #pose_position = armature.data.pose_position
 
     if anim_type == constants.OFF:
-        logger.info('Animation type not set, defaulting '\
-            'to using REST position for the armature.')
+        logger.info("Animation type not set, defaulting "\
+            "to using REST position for the armature.")
         func = _rest_bones
-        #armature.data.pose_position = 'REST'
+        #armature.data.pose_position = "REST"
     else:
         dispatch = {
             constants.REST: _rest_bones,
             constants.POSE: _pose_bones
         }
-        logger.info('Using %s for the armature', anim_type)
+        logger.info("Using %s for the armature", anim_type)
         func = dispatch[anim_type]
         #armature.data.pose_position = anim_type.upper()
 
-    bones, bone_map = func(armature, round_off, round_val)
+    bones_, bone_map = func(armature, round_off, round_val)
     #armature.data.pose_position = pose_position
 
-    return (bones, bone_map)
+    return (bones_, bone_map)
 
 
 @_mesh
 def buffer_normal(mesh, options):
+    """
+
+    :param mesh:
+    :param options:
+
+    """
     normals_ = []
     round_off, round_val = utilities.rounding(options)
 
     for face in mesh.tessfaces:
         vert_count = len(face.vertices)
         if vert_count is not 3:
-            msg = 'Non-triangulated face detected'
+            msg = "Non-triangulated face detected"
             raise exceptions.BufferGeometryError(msg)
 
         for vertex_index in face.vertices:
@@ -99,13 +129,19 @@ def buffer_normal(mesh, options):
 
 @_mesh
 def buffer_position(mesh, options):
+    """
+
+    :param mesh:
+    :param options:
+
+    """
     position = []
     round_off, round_val = utilities.rounding(options)
 
     for face in mesh.tessfaces:
         vert_count = len(face.vertices)
         if vert_count is not 3:
-            msg = 'Non-triangulated face detected'
+            msg = "Non-triangulated face detected"
             raise exceptions.BufferGeometryError(msg)
 
         for vertex_index in face.vertices:
@@ -121,44 +157,56 @@ def buffer_position(mesh, options):
 
 @_mesh
 def buffer_uv(mesh, options):
+    """
+
+    :param mesh:
+    :param options:
+
+    """
     if len(mesh.uv_layers) is 0:
         return
     elif len(mesh.uv_layers) > 1:
         # if memory serves me correctly buffer geometry
         # only uses one UV layer
-        logger.warning('%s has more than 1 UV layer', mesh.name )
+        logger.warning("%s has more than 1 UV layer", mesh.name)
 
     round_off, round_val = utilities.rounding(options)
     uvs_ = []
-    for uv in mesh.uv_layers[0].data:
-        uv = (uv.uv[0], uv.uv[1])
+    for uv_data in mesh.uv_layers[0].data:
+        uv_tuple = (uv_data.uv[0], uv_data.uv[1])
         if round_off:
-            uv = utilities.round_off(uv, round_val)
-        uvs_.extend(uv)
-    
+            uv_tuple = utilities.round_off(uv_tuple, round_val)
+        uvs_.extend(uv_tuple)
+
     return uvs_
 
 
 @_mesh
 def faces(mesh, options):
-    logger.debug('mesh.faces(%s, %s)', mesh, options)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.faces(%s, %s)", mesh, options)
     vertex_uv = len(mesh.uv_textures) > 0
     has_colors = len(mesh.vertex_colors) > 0
-    logger.info('Has UVs = %s', vertex_uv)
-    logger.info('Has vertex colours = %s', has_colors)
+    logger.info("Has UVs = %s", vertex_uv)
+    logger.info("Has vertex colours = %s", has_colors)
 
     round_off, round_val = utilities.rounding(options)
     if round_off:
-        logger.debug('Rounding off of vectors set to %s', round_off)
+        logger.debug("Rounding off of vectors set to %s", round_off)
 
     opt_colours = options[constants.COLORS] and has_colors
     opt_uvs = options[constants.UVS] and vertex_uv
     opt_materials = options.get(constants.FACE_MATERIALS)
     opt_normals = options[constants.NORMALS]
-    logger.debug('Vertex colours enabled = %s', opt_colours)
-    logger.debug('UVS enabled = %s', opt_uvs)
-    logger.debug('Materials enabled = %s', opt_materials)
-    logger.debug('Normals enabled = %s', opt_normals)
+    logger.debug("Vertex colours enabled = %s", opt_colours)
+    logger.debug("UVS enabled = %s", opt_uvs)
+    logger.debug("Materials enabled = %s", opt_materials)
+    logger.debug("Normals enabled = %s", opt_normals)
 
     uv_layers = _uvs(mesh, options) if opt_uvs else None
     vertex_normals = _normals(mesh, options) if opt_normals else None
@@ -168,41 +216,42 @@ def faces(mesh, options):
 
     colour_indices = {}
     if vertex_colours:
-        logger.debug('Indexing colours')
+        logger.debug("Indexing colours")
         for index, colour in enumerate(vertex_colours):
             colour_indices[str(colour)] = index
 
     normal_indices = {}
     if vertex_normals:
-        logger.debug('Indexing normals')
+        logger.debug("Indexing normals")
         for index, normal in enumerate(vertex_normals):
             normal_indices[str(normal)] = index
 
-    logger.info('Parsing %d faces', len(mesh.tessfaces))
+    logger.info("Parsing %d faces", len(mesh.tessfaces))
     for face in mesh.tessfaces:
         vert_count = len(face.vertices)
 
         if vert_count not in (3, 4):
-            logger.error('%d vertices for face %d detected', 
-                vert_count, face.index)
-            raise exceptions.NGonError('ngons are not supported')
+            logger.error("%d vertices for face %d detected",
+                         vert_count,
+                         face.index)
+            raise exceptions.NGonError("ngons are not supported")
 
-        materials = face.material_index is not None and opt_materials
+        mat_index = face.material_index is not None and opt_materials
         mask = {
             constants.QUAD: vert_count is 4,
-            constants.MATERIALS: materials,
+            constants.MATERIALS: mat_index,
             constants.UVS: False,
             constants.NORMALS: False,
             constants.COLORS: False
         }
 
         face_data = []
-        
+
         face_data.extend([v for v in face.vertices])
-        
+
         if mask[constants.MATERIALS]:
             face_data.append(face.material_index)
-        
+
         #@TODO: this needs the same optimization as what
         #       was done for colours and normals
         if uv_layers:
@@ -210,10 +259,11 @@ def faces(mesh, options):
                 layer = mesh.tessface_uv_textures[index]
 
                 for uv_data in layer.data[face.index].uv:
-                    uv = (uv_data[0], uv_data[1])
+                    uv_tuple = (uv_data[0], uv_data[1])
                     if round_off:
-                        uv = utilities.round_off(uv, round_val)
-                    face_data.append(uv_layer.index(uv))
+                        uv_tuple = utilities.round_off(
+                            uv_tuple, round_val)
+                    face_data.append(uv_layer.index(uv_tuple))
                     mask[constants.UVS] = True
 
         if vertex_normals:
@@ -224,7 +274,7 @@ def faces(mesh, options):
                     normal = utilities.round_off(normal, round_val)
                 face_data.append(normal_indices[str(normal)])
                 mask[constants.NORMALS] = True
-        
+
         if vertex_colours:
             colours = mesh.tessface_vertex_colors.active.data[face.index]
 
@@ -241,51 +291,59 @@ def faces(mesh, options):
         faces_data.extend(face_data)
 
     return faces_data
- 
+
 
 @_mesh
 def morph_targets(mesh, options):
-    logger.debug('mesh.morph_targets(%s, %s)', mesh, options)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.morph_targets(%s, %s)", mesh, options)
     obj = object_.objects_using_mesh(mesh)[0]
     original_frame = context.scene.frame_current
     frame_step = options.get(constants.FRAME_STEP, 1)
     scene_frames = range(context.scene.frame_start,
-        context.scene.frame_end+1, frame_step)
+                         context.scene.frame_end+1,
+                         frame_step)
 
     morphs = []
     round_off, round_val = utilities.rounding(options)
 
     for frame in scene_frames:
-        logger.info('Processing data at frame %d', frame)
+        logger.info("Processing data at frame %d", frame)
         context.scene.frame_set(frame, 0.0)
         morphs.append([])
-        vertices = object_.extract_mesh(obj, options).vertices[:]
+        vertices_ = object_.extract_mesh(obj, options).vertices[:]
 
-        for vertex in vertices:
+        for vertex in vertices_:
             if round_off:
                 vectors = [
-                    utilities.round_off(vertex.co.x, round_val), 
-                    utilities.round_off(vertex.co.y, round_val), 
+                    utilities.round_off(vertex.co.x, round_val),
+                    utilities.round_off(vertex.co.y, round_val),
                     utilities.round_off(vertex.co.z, round_val)
                 ]
             else:
                 vectors = [vertex.co.x, vertex.co.y, vertex.co.z]
             morphs[-1].extend(vectors)
-    
+
     context.scene.frame_set(original_frame, 0.0)
     morphs_detected = False
     for index, each in enumerate(morphs):
-        if index is 0: continue
+        if index is 0:
+            continue
         morphs_detected = morphs[index-1] != each
-        if morphs_detected: 
-            logger.info('Valid morph target data detected')
+        if morphs_detected:
+            logger.info("Valid morph target data detected")
             break
-    else: 
-        logger.info('No valid morph data detected')
+    else:
+        logger.info("No valid morph data detected")
         return
 
     manifest = []
-    for index,morph in enumerate(morphs):
+    for index, morph in enumerate(morphs):
         manifest.append({
             constants.NAME: 'animation_%06d' % index,
             constants.VERTICES: morph
@@ -296,25 +354,31 @@ def morph_targets(mesh, options):
 
 @_mesh
 def materials(mesh, options):
-    logger.debug('mesh.materials(%s, %s)', mesh, options)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.materials(%s, %s)", mesh, options)
     indices = set([face.material_index for face in mesh.tessfaces])
     material_sets = [(mesh.materials[index], index) for index in indices]
-    materials = []
+    materials_ = []
 
     maps = options.get(constants.MAPS)
 
     mix = options.get(constants.MIX_COLORS)
     use_colors = options.get(constants.COLORS)
-    logger.info('Colour mix is set to %s', mix)
-    logger.info('Vertex colours set to %s', use_colors)
+    logger.info("Colour mix is set to %s", mix)
+    logger.info("Vertex colours set to %s", use_colors)
 
     for mat, index in material_sets:
         try:
             dbg_color = constants.DBG_COLORS[index]
         except IndexError:
             dbg_color = constants.DBG_COLORS[0]
-        
-        logger.info('Compiling attributes for %s', mat.name)
+
+        logger.info("Compiling attributes for %s", mat.name)
         attributes = {
             constants.COLOR_AMBIENT: material.ambient_color(mat),
             constants.COLOR_EMISSIVE: material.emissive_color(mat),
@@ -334,58 +398,65 @@ def materials(mesh, options):
         if use_colors:
             colors = material.use_vertex_colors(mat)
             attributes[constants.VERTEX_COLORS] = colors
-                
+
         if (use_colors and mix) or (not use_colors):
             colors = material.diffuse_color(mat)
             attributes[constants.COLOR_DIFFUSE] = colors
 
         if attributes[constants.SHADING] == constants.PHONG:
-            logger.info('Adding specular attributes')
+            logger.info("Adding specular attributes")
             attributes.update({
                 constants.SPECULAR_COEF: material.specular_coef(mat),
                 constants.COLOR_SPECULAR: material.specular_color(mat)
             })
 
         if mesh.show_double_sided:
-            logger.info('Double sided is on')
+            logger.info("Double sided is on")
             attributes[constants.DOUBLE_SIDED] = True
 
-        materials.append(attributes)
+        materials_.append(attributes)
 
-        if not maps: continue
+        if not maps:
+            continue
 
         diffuse = _diffuse_map(mat)
         if diffuse:
-            logger.info('Diffuse map found')
+            logger.info("Diffuse map found")
             attributes.update(diffuse)
-        
+
         light = _light_map(mat)
         if light:
-            logger.info('Light map found')
+            logger.info("Light map found")
             attributes.update(light)
 
         specular = _specular_map(mat)
         if specular:
-            logger.info('Specular map found')
+            logger.info("Specular map found")
             attributes.update(specular)
 
         if attributes[constants.SHADING] == constants.PHONG:
             normal = _normal_map(mat)
             if normal:
-                logger.info('Normal map found')
+                logger.info("Normal map found")
                 attributes.update(normal)
 
             bump = _bump_map(mat)
             if bump:
-                logger.info('Bump map found')
+                logger.info("Bump map found")
                 attributes.update(bump)
 
-    return materials
+    return materials_
 
 
 @_mesh
 def normals(mesh, options):
-    logger.debug('mesh.normals(%s, %s)', mesh, options)
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.normals(%s, %s)", mesh, options)
     normal_vectors = []
 
     for vector in _normals(mesh, options):
@@ -396,86 +467,122 @@ def normals(mesh, options):
 
 @_mesh
 def skin_weights(mesh, bone_map, influences):
-    logger.debug('mesh.skin_weights(%s)', mesh)
+    """
+
+    :param mesh:
+    :param bone_map:
+    :param influences:
+
+    """
+    logger.debug("mesh.skin_weights(%s)", mesh)
     return _skinning_data(mesh, bone_map, influences, 1)
 
 
 @_mesh
 def skin_indices(mesh, bone_map, influences):
-    logger.debug('mesh.skin_indices(%s)', mesh)
+    """
+
+    :param mesh:
+    :param bone_map:
+    :param influences:
+
+    """
+    logger.debug("mesh.skin_indices(%s)", mesh)
     return _skinning_data(mesh, bone_map, influences, 0)
 
 
 @_mesh
 def texture_registration(mesh):
-    logger.debug('mesh.texture_registration(%s)', mesh)
-    materials = mesh.materials or []
+    """
+
+    :param mesh:
+
+    """
+    logger.debug("mesh.texture_registration(%s)", mesh)
+    materials_ = mesh.materials or []
     registration = {}
 
     funcs = (
-        (constants.MAP_DIFFUSE, material.diffuse_map), 
+        (constants.MAP_DIFFUSE, material.diffuse_map),
         (constants.SPECULAR_MAP, material.specular_map),
-        (constants.LIGHT_MAP, material.light_map), 
-        (constants.BUMP_MAP, material.bump_map), 
+        (constants.LIGHT_MAP, material.light_map),
+        (constants.BUMP_MAP, material.bump_map),
         (constants.NORMAL_MAP, material.normal_map)
     )
-    
+
     def _registration(file_path, file_name):
+        """
+
+        :param file_path:
+        :param file_name:
+
+        """
         return {
             'file_path': file_path,
             'file_name': file_name,
             'maps': []
         }
 
-    logger.info('found %d materials', len(materials))
-    for mat in materials:
+    logger.info("found %d materials", len(materials_))
+    for mat in materials_:
         for (key, func) in funcs:
             tex = func(mat)
-            if tex is None: continue
+            if tex is None:
+                continue
 
-            logger.info('%s has texture %s', key, tex.name)
+            logger.info("%s has texture %s", key, tex.name)
             file_path = texture.file_path(tex)
             file_name = texture.file_name(tex)
 
-            hash_ = utilities.hash(file_path)
-
-            reg = registration.setdefault(hash_, 
+            reg = registration.setdefault(
+                utilities.hash(file_path),
                 _registration(file_path, file_name))
 
-            reg['maps'].append(key)
+            reg["maps"].append(key)
 
     return registration
 
 
 @_mesh
 def uvs(mesh, options):
-    logger.debug('mesh.uvs(%s, %s)', mesh, options)
-    uvs = []
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.uvs(%s, %s)", mesh, options)
+    uvs_ = []
     for layer in _uvs(mesh, options):
-        uvs.append([])
-        logger.info('Parsing UV layer %d', len(uvs))
+        uvs_.append([])
+        logger.info("Parsing UV layer %d", len(uvs_))
         for pair in layer:
-            uvs[-1].extend(pair)
-    return uvs
+            uvs_[-1].extend(pair)
+    return uvs_
 
 
 @_mesh
 def vertex_colors(mesh):
-    logger.debug('mesh.vertex_colors(%s)', mesh)
+    """
+
+    :param mesh:
+
+    """
+    logger.debug("mesh.vertex_colors(%s)", mesh)
     vertex_colours = []
 
     try:
         vertex_colour = mesh.tessface_vertex_colors.active.data
     except AttributeError:
-        logger.info('No vertex colours found')
+        logger.info("No vertex colours found")
         return
 
     for face in mesh.tessfaces:
 
         colours = (vertex_colour[face.index].color1,
-            vertex_colour[face.index].color2,
-            vertex_colour[face.index].color3,
-            vertex_colour[face.index].color4)
+                   vertex_colour[face.index].color2,
+                   vertex_colour[face.index].color3,
+                   vertex_colour[face.index].color4)
 
         for colour in colours:
             colour = utilities.rgb2int((colour.r, colour.g, colour.b))
@@ -488,8 +595,14 @@ def vertex_colors(mesh):
 
 @_mesh
 def vertices(mesh, options):
-    logger.debug('mesh.vertices(%s, %s)', mesh, options)
-    vertices = []
+    """
+
+    :param mesh:
+    :param options:
+
+    """
+    logger.debug("mesh.vertices(%s, %s)", mesh, options)
+    vertices_ = []
 
     round_off, round_val = utilities.rounding(options)
 
@@ -498,26 +611,31 @@ def vertices(mesh, options):
         if round_off:
             vector = utilities.round_off(vector, round_val)
 
-        vertices.extend(vector)
+        vertices_.extend(vector)
 
-    return vertices
+    return vertices_
 
 
 def _normal_map(mat):
+    """
+
+    :param mat:
+
+    """
     tex = material.normal_map(mat)
     if tex is None:
         return
 
-    logger.info('Found normal texture map %s', tex.name)
+    logger.info("Found normal texture map %s", tex.name)
 
     normal = {
-        constants.MAP_NORMAL: 
+        constants.MAP_NORMAL:
             texture.file_name(tex),
         constants.MAP_NORMAL_FACTOR:
-            material.normal_scale(mat), 
-        constants.MAP_NORMAL_ANISOTROPY: 
+            material.normal_scale(mat),
+        constants.MAP_NORMAL_ANISOTROPY:
             texture.anisotropy(tex),
-        constants.MAP_NORMAL_WRAP: texture.wrap(tex), 
+        constants.MAP_NORMAL_WRAP: texture.wrap(tex),
         constants.MAP_NORMAL_REPEAT: texture.repeat(tex)
     }
 
@@ -525,75 +643,95 @@ def _normal_map(mat):
 
 
 def _bump_map(mat):
+    """
+
+    :param mat:
+
+    """
     tex = material.bump_map(mat)
     if tex is None:
         return
 
-    logger.info('Found bump texture map %s', tex.name)
+    logger.info("Found bump texture map %s", tex.name)
 
     bump = {
-        constants.MAP_BUMP: 
+        constants.MAP_BUMP:
             texture.file_name(tex),
-        constants.MAP_BUMP_ANISOTROPY: 
+        constants.MAP_BUMP_ANISOTROPY:
             texture.anisotropy(tex),
         constants.MAP_BUMP_WRAP: texture.wrap(tex),
         constants.MAP_BUMP_REPEAT: texture.repeat(tex),
         constants.MAP_BUMP_SCALE:
-            material.bump_scale(mat), 
+            material.bump_scale(mat),
     }
 
     return bump
 
 
 def _specular_map(mat):
+    """
+
+    :param mat:
+
+    """
     tex = material.specular_map(mat)
     if tex is None:
-        return 
+        return
 
-    logger.info('Found specular texture map %s', tex.name)
+    logger.info("Found specular texture map %s", tex.name)
 
     specular = {
-        constants.MAP_SPECULAR: 
+        constants.MAP_SPECULAR:
             texture.file_name(tex),
-        constants.MAP_SPECULAR_ANISOTROPY: 
+        constants.MAP_SPECULAR_ANISOTROPY:
             texture.anisotropy(tex),
         constants.MAP_SPECULAR_WRAP: texture.wrap(tex),
         constants.MAP_SPECULAR_REPEAT: texture.repeat(tex)
     }
 
-    return specular 
+    return specular
 
 
 def _light_map(mat):
+    """
+
+    :param mat:
+
+    """
     tex = material.light_map(mat)
     if tex is None:
-        return 
+        return
 
-    logger.info('Found light texture map %s', tex.name)
+    logger.info("Found light texture map %s", tex.name)
 
     light = {
-        constants.MAP_LIGHT: 
+        constants.MAP_LIGHT:
             texture.file_name(tex),
-        constants.MAP_LIGHT_ANISOTROPY: 
+        constants.MAP_LIGHT_ANISOTROPY:
             texture.anisotropy(tex),
         constants.MAP_LIGHT_WRAP: texture.wrap(tex),
         constants.MAP_LIGHT_REPEAT: texture.repeat(tex)
     }
 
-    return light 
+    return light
 
 
 def _diffuse_map(mat):
+    """
+
+    :param mat:
+
+    """
     tex = material.diffuse_map(mat)
     if tex is None:
-        return 
+        return
 
-    logger.info('Found diffuse texture map %s', tex.name)
+    logger.info("Found diffuse texture map %s", tex.name)
 
     diffuse = {
-        constants.MAP_DIFFUSE: 
+        constants.MAP_DIFFUSE:
             texture.file_name(tex),
-        constants.MAP_DIFFUSE_ANISOTROPY: 
+        constants.MAP_DIFFUSE_ANISOTROPY:
             texture.anisotropy(tex),
         constants.MAP_DIFFUSE_WRAP: texture.wrap(tex),
         constants.MAP_DIFFUSE_REPEAT: texture.repeat(tex)
@@ -603,6 +741,12 @@ def _diffuse_map(mat):
 
 
 def _normals(mesh, options):
+    """
+
+    :param mesh:
+    :param options:
+
+    """
     vectors = []
     round_off, round_val = utilities.rounding(options)
 
@@ -626,41 +770,60 @@ def _normals(mesh, options):
 
 
 def _uvs(mesh, options):
+    """
+
+    :param mesh:
+    :param options:
+
+    """
     uv_layers = []
     round_off, round_val = utilities.rounding(options)
 
     for layer in mesh.uv_layers:
         uv_layers.append([])
 
-        for uv in layer.data:
-            uv = (uv.uv[0], uv.uv[1])
+        for uv_data in layer.data:
+            uv_tuple = (uv_data.uv[0], uv_data.uv[1])
             if round_off:
-                uv = utilities.round_off(uv, round_val)
+                uv_tuple = utilities.round_off(uv_tuple, round_val)
 
-            if uv not in uv_layers[-1]:
-                uv_layers[-1].append(uv)
+            if uv_tuple not in uv_layers[-1]:
+                uv_layers[-1].append(uv_tuple)
 
     return uv_layers
 
 
 def _armature(mesh):
+    """
+
+    :param mesh:
+
+    """
     obj = object_.objects_using_mesh(mesh)[0]
     armature = obj.find_armature()
     if armature:
-        logger.info('Found armature %s for %s', armature.name, obj.name)
+        logger.info("Found armature %s for %s", armature.name, obj.name)
     else:
-        logger.info('Found no armature for %s', obj.name)
+        logger.info("Found no armature for %s", obj.name)
     return armature
 
 
 def _skinning_data(mesh, bone_map, influences, array_index):
+    """
+
+    :param mesh:
+    :param bone_map:
+    :param influences:
+    :param array_index:
+
+    """
     armature = _armature(mesh)
     manifest = []
-    if not armature: 
+    if not armature:
         return manifest
 
     obj = object_.objects_using_mesh(mesh)[0]
-    logger.debug('Skinned object found %s', obj.name)
+    logger.debug("Skinned object found %s", obj.name)
 
     for vertex in mesh.vertices:
         bone_array = []
@@ -691,7 +854,14 @@ def _skinning_data(mesh, bone_map, influences, array_index):
 
 
 def _pose_bones(armature, round_off, round_val):
-    bones = []
+    """
+
+    :param armature:
+    :param round_off:
+    :param round_val:
+
+    """
+    bones_ = []
     bone_map = {}
     bone_count = 0
 
@@ -727,7 +897,7 @@ def _pose_bones(armature, round_off, round_val):
             )
             rot = (
                 utilities.round_off(rot.x, round_val),
-                utilities.round_off(rot.z, round_val), 
+                utilities.round_off(rot.z, round_val),
                 -utilities.round_off(rot.y, round_val),
                 utilities.round_off(rot.w, round_val)
             )
@@ -740,29 +910,36 @@ def _pose_bones(armature, round_off, round_val):
             pos = (pos.x, pos.z, -pos.y)
             rot = (rot.x, rot.z, -rot.y, rot.w)
             scl = (scl.x, scl.z, scl.y)
-        bones.append({
+        bones_.append({
             constants.PARENT: bone_index,
             constants.NAME: armature_bone.name,
             constants.POS: pos,
             constants.ROTQ: rot,
-            constants.SCL: scl 
+            constants.SCL: scl
         })
 
-    return bones, bone_map
+    return bones_, bone_map
 
 
 def _rest_bones(armature, round_off, round_val):
-    bones = []
+    """
+
+    :param armature:
+    :param round_off:
+    :param round_val:
+
+    """
+    bones_ = []
     bone_map = {}
     bone_count = 0
     bone_index_rel = 0
 
     for bone in armature.data.bones:
-        logger.info('Parsing bone %s', bone.name)
+        logger.info("Parsing bone %s", bone.name)
 
         if not bone.use_deform:
-            logger.debug('Ignoring bone %s at: %d', 
-                bone.name, bone_index_rel)
+            logger.debug("Ignoring bone %s at: %d",
+                         bone.name, bone_index_rel)
             continue
 
         if bone.parent is None:
@@ -787,21 +964,21 @@ def _rest_bones(armature, round_off, round_val):
             y_axis = bone_world_pos.z
             z_axis = -bone_world_pos.y
 
-        logger.debug('Adding bone %s at: %s, %s', 
-            bone.name, bone_index, bone_index_rel)
+        logger.debug("Adding bone %s at: %s, %s",
+                     bone.name, bone_index, bone_index_rel)
         bone_map[bone_count] = bone_index_rel
         bone_index_rel += 1
         #@TODO: the rotq probably should not have these
         #       hard coded values
-        bones.append({
+        bones_.append({
             constants.PARENT: bone_index,
             constants.NAME: bone.name,
             constants.POS: (x_axis, y_axis, z_axis),
-            constants.ROTQ: (0,0,0,1)
+            constants.ROTQ: (0, 0, 0, 1)
         })
 
         bone_count += 1
 
-    return (bones, bone_map)
+    return (bones_, bone_map)
 
 

+ 217 - 70
utils/exporters/blender/addons/io_three/exporter/api/object.py

@@ -17,12 +17,10 @@ from .constants import (
     PERSP,
     ORTHO,
     RENDER,
-    NO_SHADOW
+    NO_SHADOW,
+    ZYX
 )
 
-ROTATE_X_PI2 = mathutils.Quaternion((1.0, 0.0, 0.0), 
-    math.radians(-90.0)).to_matrix().to_4x4()
-
 
 # Blender doesn't seem to have a good way to link a mesh back to the
 # objects that are instancing it, or it is bloody obvious and I haven't
@@ -32,20 +30,43 @@ _MESH_MAP = {}
 
 
 def _object(func):
+    """
+
+    :param func:
+
+    """
+
+    def inner(arg, *args, **kwargs):
+        """
 
-    def inner(name, *args, **kwargs):
+        :param arg:
+        :param *args:
+        :param **kwargs:
 
-        if isinstance(name, types.Object):
-            obj = name
+        """
+
+        if isinstance(arg, types.Object):
+            obj = arg
         else:
-            obj = data.objects[name]
+            obj = data.objects[arg]
 
         return func(obj, *args, **kwargs)
 
     return inner
 
 
+def clear_mesh_map():
+    """Clears the mesh map, required on initialization"""
+    _MESH_MAP.clear()
+
+
 def assemblies(valid_types, options):
+    """
+
+    :param valid_types:
+    :param options:
+
+    """
     logger.debug('object.assemblies(%s)', valid_types)
     for obj in data.objects:
 
@@ -60,6 +81,11 @@ def assemblies(valid_types, options):
 
 @_object
 def cast_shadow(obj):
+    """
+
+    :param obj:
+
+    """
     logger.debug('object.cast_shadow(%s)', obj)
     if obj.type == LAMP:
         if obj.data.type in (SPOT, SUN):
@@ -79,6 +105,12 @@ def cast_shadow(obj):
 
 @_object
 def children(obj, valid_types):
+    """
+
+    :param obj:
+    :param valid_types:
+
+    """
     logger.debug('object.children(%s, %s)', obj, valid_types)
     for child in obj.children:
         if child.type in valid_types:
@@ -87,6 +119,11 @@ def children(obj, valid_types):
 
 @_object
 def material(obj):
+    """
+
+    :param obj:
+
+    """
     logger.debug('object.material(%s)', obj)
     try:
         return obj.material_slots[0].name
@@ -96,38 +133,54 @@ def material(obj):
 
 @_object
 def mesh(obj, options):
+    """
+
+    :param obj:
+    :param options:
+
+    """
     logger.debug('object.mesh(%s, %s)', obj, options)
     if obj.type != MESH:
         return
-    
-    for mesh, objects in _MESH_MAP.items():
+
+    for mesh_, objects in _MESH_MAP.items():
         if obj in objects:
-            return mesh
+            return mesh_
     else:
         logger.debug('Could not map object, updating manifest')
-        mesh = extract_mesh(obj, options)
-        if len(mesh.tessfaces) is not 0:
-            manifest = _MESH_MAP.setdefault(mesh.name, [])
+        mesh_ = extract_mesh(obj, options)
+        if len(mesh_.tessfaces) is not 0:
+            manifest = _MESH_MAP.setdefault(mesh_.name, [])
             manifest.append(obj)
-            mesh = mesh.name
+            mesh_name = mesh_.name
         else:
             # possibly just being used as a controller
             logger.info('Object %s has no faces', obj.name)
-            mesh = None
+            mesh_name = None
 
-    return mesh
+    return mesh_name
 
 
 @_object
 def name(obj):
+    """
+
+    :param obj:
+
+    """
     return obj.name
 
 
 @_object
 def node_type(obj):
+    """
+
+    :param obj:
+
+    """
     logger.debug('object.node_type(%s)', obj)
     # standard transformation nodes are inferred
-    if obj.type == MESH: 
+    if obj.type == MESH:
         return constants.MESH.title()
     elif obj.type == EMPTY:
         return constants.OBJECT.title()
@@ -150,18 +203,29 @@ def node_type(obj):
     except AttributeError:
         msg = 'Invalid type: %s' % obj.type
         raise exceptions.UnsupportedObjectType(msg)
- 
+
 
 def nodes(valid_types, options):
+    """
+
+    :param valid_types:
+    :param options:
+
+    """
     for obj in data.objects:
         if _valid_node(obj, valid_types, options):
             yield obj.name
 
 @_object
 def position(obj, options):
+    """
+
+    :param obj:
+    :param options:
+
+    """
     logger.debug('object.position(%s)', obj)
-    parent = obj.parent is None
-    vector = _decompose_matrix(obj, local=not parent)[0]
+    vector = _decompose_matrix(obj)[0]
     vector = (vector.x, vector.y, vector.z)
 
     round_off, round_val = utilities.rounding(options)
@@ -173,6 +237,11 @@ def position(obj, options):
 
 @_object
 def receive_shadow(obj):
+    """
+
+    :param obj:
+
+    """
     if obj.type == MESH:
         mat = material(obj)
         if mat:
@@ -183,9 +252,15 @@ def receive_shadow(obj):
 
 @_object
 def rotation(obj, options):
+    """
+
+    :param obj:
+    :param options:
+
+    """
     logger.debug('object.rotation(%s)', obj)
-    vector = _decompose_matrix(obj)[1]
-    vector = (vector.x, vector.y, vector.z, vector.w)
+    vector = _decompose_matrix(obj)[1].to_euler(ZYX)
+    vector = (vector.x, vector.y, vector.z)
 
     round_off, round_val = utilities.rounding(options)
     if round_off:
@@ -196,6 +271,12 @@ def rotation(obj, options):
 
 @_object
 def scale(obj, options):
+    """
+
+    :param obj:
+    :param options:
+
+    """
     logger.debug('object.scale(%s)', obj)
     vector = _decompose_matrix(obj)[2]
     vector = (vector.x, vector.y, vector.z)
@@ -209,56 +290,80 @@ def scale(obj, options):
 
 @_object
 def select(obj):
+    """
+
+    :param obj:
+
+    """
     obj.select = True
 
 
 @_object
 def unselect(obj):
+    """
+
+    :param obj:
+
+    """
     obj.select = False
 
 
 @_object
 def visible(obj):
+    """
+
+    :param obj:
+
+    """
     logger.debug('object.visible(%s)', obj)
     return obj.is_visible(context.scene)
 
 
 def extract_mesh(obj, options, recalculate=False):
+    """
+
+    :param obj:
+    :param options:
+    :param recalculate:  (Default value = False)
+
+    """
     logger.debug('object.extract_mesh(%s, %s)', obj, options)
-    mesh = obj.to_mesh(context.scene, True, RENDER)
+    mesh_node = obj.to_mesh(context.scene, True, RENDER)
 
     # transfer the geometry type to the extracted mesh
-    mesh.THREE_geometry_type = obj.data.THREE_geometry_type
+    mesh_node.THREE_geometry_type = obj.data.THREE_geometry_type
 
     # now determine whether or not to export using the geometry type
     # set globally from the exporter's options or to use the local
     # override on the mesh node itself
-    opt_buffer = options.get(constants.GEOMETRY_TYPE) 
+    opt_buffer = options.get(constants.GEOMETRY_TYPE)
     opt_buffer = opt_buffer == constants.BUFFER_GEOMETRY
-    prop_buffer = mesh.THREE_geometry_type == constants.BUFFER_GEOMETRY
+    prop_buffer = mesh_node.THREE_geometry_type == constants.BUFFER_GEOMETRY
 
     # if doing buffer geometry it is imperative to triangulate the mesh
     if opt_buffer or prop_buffer:
         original_mesh = obj.data
-        obj.data = mesh
-        logger.debug('swapped %s for %s', original_mesh.name, mesh.name)
-    
+        obj.data = mesh_node
+        logger.debug('swapped %s for %s',
+                     original_mesh.name,
+                     mesh_node.name)
+
         obj.select = True
         bpy.context.scene.objects.active = obj
         logger.info('Applying triangulation to %s', obj.data.name)
         bpy.ops.object.modifier_add(type='TRIANGULATE')
-        bpy.ops.object.modifier_apply(apply_as='DATA', 
-            modifier='Triangulate')
+        bpy.ops.object.modifier_apply(apply_as='DATA',
+                                      modifier='Triangulate')
         obj.data = original_mesh
         obj.select = False
 
     # recalculate the normals to face outwards, this is usually
-    # best after applying a modifiers, especialy for something 
+    # best after applying a modifiers, especialy for something
     # like the mirror
     if recalculate:
         logger.info('Recalculating normals')
         original_mesh = obj.data
-        obj.data = mesh
+        obj.data = mesh_node
 
         bpy.context.scene.objects.active = obj
         bpy.ops.object.mode_set(mode='EDIT')
@@ -270,64 +375,72 @@ def extract_mesh(obj, options, recalculate=False):
 
     if not options.get(constants.SCENE):
         xrot = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X')
-        mesh.transform(xrot * obj.matrix_world)
+        mesh_node.transform(xrot * obj.matrix_world)
 
     # now generate a unique name
     index = 0
     while True:
         if index is 0:
-            name = '%sGeometry' % obj.data.name
+            mesh_name = '%sGeometry' % obj.data.name
         else:
-            name = '%sGeometry.%d' % (obj.data.name, index)
+            mesh_name = '%sGeometry.%d' % (obj.data.name, index)
         try:
-            data.meshes[name]
+            data.meshes[mesh_name]
             index += 1
         except KeyError:
             break
-    mesh.name = name
+    mesh_node.name = mesh_name
+
+    mesh_node.update(calc_tessface=True)
+    mesh_node.calc_normals()
+    mesh_node.calc_tessface()
+    scale_ = options.get(constants.SCALE, 1)
+    mesh_node.transform(mathutils.Matrix.Scale(scale_, 4))
+
+    return mesh_node
 
-    mesh.update(calc_tessface=True)
-    mesh.calc_normals()
-    mesh.calc_tessface()
-    scale = options.get(constants.SCALE, 1)
-    mesh.transform(mathutils.Matrix.Scale(scale, 4))
 
-    return mesh
+def objects_using_mesh(mesh_node):
+    """
 
+    :param mesh_node:
+    :return: list of object names
 
-def objects_using_mesh(mesh):
-    logger.debug('object.objects_using_mesh(%s)', mesh)
-    for name, objects in _MESH_MAP.items():
-        if name == mesh.name:
+    """
+    logger.debug('object.objects_using_mesh(%s)', mesh_node)
+    for mesh_name, objects in _MESH_MAP.items():
+        if mesh_name == mesh_node.name:
             return objects
     else:
         logger.warning('Could not find mesh mapping')
 
 
 def prep_meshes(options):
-    '''
-    Prep the mesh nodes. Preperation includes identifying:
+    """Prep the mesh nodes. Preperation includes identifying:
         - nodes that are on visible layers
         - nodes that have export disabled
         - nodes that have modifiers that need to be applied
-    '''
+
+    :param options:
+
+    """
     logger.debug('object.prep_meshes(%s)', options)
     mapping = {}
 
     visible_layers = _visible_scene_layers()
-        
+
     for obj in data.objects:
-        if obj.type != MESH: 
+        if obj.type != MESH:
             continue
 
         # this is ideal for skipping controller or proxy nodes
         # that may apply to a Blender but not a 3js scene
-        if not _on_visible_layer(obj, visible_layers): 
+        if not _on_visible_layer(obj, visible_layers):
             logger.info('%s is not on a visible layer', obj.name)
             continue
 
         # if someone really insists on a visible node not being exportable
-        if not obj.THREE_export: 
+        if not obj.THREE_export:
             logger.info('%s export is disabled', obj.name)
             continue
 
@@ -338,59 +451,93 @@ def prep_meshes(options):
         # the mesh making the mesh unique to this particular object
         if len(obj.modifiers):
             logger.info('%s has modifiers' % obj.name)
-            mesh = extract_mesh(obj, options, recalculate=True)
-            _MESH_MAP[mesh.name] = [obj]
+            mesh_node = extract_mesh(obj, options, recalculate=True)
+            _MESH_MAP[mesh_node.name] = [obj]
             continue
 
-        logger.info('adding mesh %s.%s to prep', 
-            obj.name, obj.data.name)
+        logger.info('adding mesh %s.%s to prep',
+                    obj.name, obj.data.name)
         manifest = mapping.setdefault(obj.data.name, [])
         manifest.append(obj)
-    
+
     # now associate the extracted mesh node with all the objects
     # that are instancing it
     for objects in mapping.values():
-        mesh = extract_mesh(objects[0], options)
-        _MESH_MAP[mesh.name] = objects
+        mesh_node = extract_mesh(objects[0], options)
+        _MESH_MAP[mesh_node.name] = objects
 
 
 def extracted_meshes():
+    """
+
+    :return: names of extracted mesh nodes
+
+    """
     logger.debug('object.extracted_meshes()')
     return [key for key in _MESH_MAP.keys()]
 
 
 def _decompose_matrix(obj, local=False):
+    """
+
+    :param obj:
+    :param local:  (Default value = False)
+
+    """
+    rotate_x_pi2 = mathutils.Quaternion((1.0, 0.0, 0.0),
+                                        math.radians(-90.0))
+    rotate_x_pi2 = rotate_x_pi2.to_matrix().to_4x4()
+
     if local:
-        matrix = ROTATE_X_PI2 * obj.matrix_local
+        matrix = rotate_x_pi2 * obj.matrix_local
     else:
-        matrix = ROTATE_X_PI2 * obj.matrix_world
+        matrix = rotate_x_pi2 * obj.matrix_world
     return matrix.decompose()
 
 
 def _on_visible_layer(obj, visible_layers):
-    visible = True
+    """
+
+    :param obj:
+    :param visible_layers:
+
+    """
+    is_visible = True
     for index, layer in enumerate(obj.layers):
         if layer and index not in visible_layers:
             logger.info('%s is on a hidden layer', obj.name)
-            visible = False
+            is_visible = False
             break
-    return visible
+    return is_visible
 
 
 def _visible_scene_layers():
+    """
+
+    :return: list of visiible layer indices
+
+    """
     visible_layers = []
     for index, layer in enumerate(context.scene.layers):
-        if layer: visible_layers.append(index)
+        if layer:
+            visible_layers.append(index)
     return visible_layers
 
 
 def _valid_node(obj, valid_types, options):
+    """
+
+    :param obj:
+    :param valid_types:
+    :param options:
+
+    """
     if obj.type not in valid_types:
         return False
 
     # skip objects that are not on visible layers
     visible_layers = _visible_scene_layers()
-    if not _on_visible_layer(obj, visible_layers): 
+    if not _on_visible_layer(obj, visible_layers):
         return False
 
     try:

+ 91 - 17
utils/exporters/blender/addons/io_three/exporter/api/texture.py

@@ -5,13 +5,25 @@ from . import image
 
 
 def _texture(func):
+    """
+
+    :param func:
+
+    """
 
     def inner(name, *args, **kwargs):
+        """
+
+        :param name:
+        :param *args:
+        :param **kwargs:
+
+        """
 
         if isinstance(name, types.Texture):
             texture = name
         else:
-            texture = data.textures[name] 
+            texture = data.textures[name]
 
         return func(texture, *args, **kwargs)
 
@@ -20,37 +32,67 @@ def _texture(func):
 
 @_texture
 def anisotropy(texture):
-    logger.debug('texture.file_path(%s)', texture)
+    """
+
+    :param texture:
+    :return: filter_size value
+
+    """
+    logger.debug("texture.file_path(%s)", texture)
     return texture.filter_size
 
 
 @_texture
 def file_name(texture):
-    logger.debug('texture.file_name(%s)', texture)
+    """
+
+    :param texture:
+    :return: file name
+
+    """
+    logger.debug("texture.file_name(%s)", texture)
     if texture.image:
         return image.file_name(texture.image)
 
 
 @_texture
 def file_path(texture):
-    logger.debug('texture.file_path(%s)', texture)
+    """
+
+    :param texture:
+    :return: file path
+
+    """
+    logger.debug("texture.file_path(%s)", texture)
     if texture.image:
         return image.file_path(texture.image)
 
 
 @_texture
 def image_node(texture):
-    logger.debug('texture.image_node(%s)', texture)
+    """
+
+    :param texture:
+    :return: texture's image node
+
+    """
+    logger.debug("texture.image_node(%s)", texture)
     return texture.image
 
 
 @_texture
 def mag_filter(texture):
-    logger.debug('texture.mag_filter(%s)', texture)
+    """
+
+    :param texture:
+    :return: THREE_mag_filter value
+
+    """
+    logger.debug("texture.mag_filter(%s)", texture)
     try:
         val = texture.THREE_mag_filter
     except AttributeError:
-        logger.debug('No THREE_mag_filter attribute found')
+        logger.debug("No THREE_mag_filter attribute found")
         val = MAG_FILTER
 
     return val
@@ -58,44 +100,76 @@ def mag_filter(texture):
 
 @_texture
 def mapping(texture):
-    logger.debug('texture.mapping(%s)', texture)
+    """
+
+    :param texture:
+    :return: THREE_mapping value
+
+    """
+    logger.debug("texture.mapping(%s)", texture)
     try:
         val = texture.THREE_mapping
     except AttributeError:
-        logger.debug('No THREE_mapping attribute found')
+        logger.debug("No THREE_mapping attribute found")
         val = MAPPING
 
     return val
+
+
 @_texture
 def min_filter(texture):
-    logger.debug('texture.min_filter(%s)', texture)
+    """
+
+    :param texture:
+    :return: THREE_min_filter value
+
+    """
+    logger.debug("texture.min_filter(%s)", texture)
     try:
         val = texture.THREE_min_filter
     except AttributeError:
-        logger.debug('No THREE_min_filter attribute found')
-        val = MIN_FILTER 
+        logger.debug("No THREE_min_filter attribute found")
+        val = MIN_FILTER
 
     return val
 
 
 @_texture
 def repeat(texture):
-    logger.debug('texture.repeat(%s)', texture)
+    """The repeat parameters of the texture node
+
+    :param texture:
+    :returns: repeat_x, and repeat_y values
+
+    """
+    logger.debug("texture.repeat(%s)", texture)
     return (texture.repeat_x, texture.repeat_y)
 
 
 @_texture
 def wrap(texture):
-    logger.debug('texture.wrap(%s)', texture)
+    """The wrapping parameters of the texture node
+
+    :param texture:
+    :returns: tuple of THREE compatible wrapping values
+
+    """
+    logger.debug("texture.wrap(%s)", texture)
     wrapping = {
-        True: constants.WRAPPING.MIRROR, 
+        True: constants.WRAPPING.MIRROR,
         False: constants.WRAPPING.REPEAT
     }
-    return (wrapping[texture.use_mirror_x], wrapping[texture.use_mirror_y])
+    return (wrapping[texture.use_mirror_x],
+            wrapping[texture.use_mirror_y])
 
 
 def textures():
-    logger.debug('texture.textures()')
+    """
+
+    :return: list of texture node names that are IMAGE
+
+    """
+    logger.debug("texture.textures()")
     for texture in data.textures:
         if texture.type == IMAGE:
             yield texture.name

+ 4 - 2
utils/exporters/blender/addons/io_three/exporter/geometry.py

@@ -115,7 +115,8 @@ class Geometry(base_classes.BaseNode):
     def copy(self, scene=True):
         """Copy the geometry definitions to a standard dictionary.
 
-        :param scene: toggle for scene formatting (Default = True)
+        :param scene: toggle for scene formatting 
+                      (Default value = True)
         :type scene: bool
         :rtype: dict
 
@@ -168,7 +169,8 @@ class Geometry(base_classes.BaseNode):
         """Write the geometry definitions to disk. Uses the
         desitnation path of the scene.
 
-        :param filepath: optional output file path (Default=None)
+        :param filepath: optional output file path 
+                        (Default value = None)
         :type filepath: str
 
         """

+ 1 - 1
utils/exporters/blender/addons/io_three/exporter/image.py

@@ -36,7 +36,7 @@ class Image(base_classes.BaseNode):
         """Copy the texture.
         self.filepath > self.destination
 
-        :param func: Optional function override (Default = io.copy)
+        :param func: Optional function override (Default value = io.copy)
                      arguments are (<source>, <destination>)
         :return: path the texture was copied to
 

+ 1 - 1
utils/exporters/blender/addons/io_three/exporter/io.py

@@ -39,7 +39,7 @@ def dump(filepath, data, options=None):
 
     :param filepath: output file path
     :param data: serializable data to write to disk
-    :param options: (Default = None)
+    :param options: (Default value = None)
     :type options: dict
 
     """

+ 5 - 5
utils/exporters/blender/addons/io_three/exporter/object.py

@@ -110,11 +110,11 @@ class Object(base_classes.BaseNode):
         elif self[constants.TYPE] in lights:
             self._init_light()
 
-        for child in api.object.children(self.node, self.scene.valid_types):
-            if not self.get(constants.CHILDREN):
-                self[constants.CHILDREN] = [Object(child, parent=self)]
-            else:
-                self[constants.CHILDREN].append(Object(child, parent=self))
+        #for child in api.object.children(self.node, self.scene.valid_types):
+        #    if not self.get(constants.CHILDREN):
+        #        self[constants.CHILDREN] = [Object(child, parent=self)]
+        #    else:
+        #        self[constants.CHILDREN].append(Object(child, parent=self))
 
     def _root_setup(self):
         """Applies to a root/scene object"""

+ 1 - 1
utils/exporters/blender/addons/io_three/exporter/scene.py

@@ -206,7 +206,7 @@ class Scene(base_classes.BaseScene):
         self[constants.UUID] = utilities.id_from_name(scene_name)
 
         objects = []
-        for node in api.object.assemblies(self.valid_types, self.options):
+        for node in api.object.nodes(self.valid_types, self.options):
             logger.info("Parsing object %s", node)
             obj = object_.Object(node, parent=self[constants.OBJECT])
             objects.append(obj)

+ 1 - 1
utils/exporters/blender/addons/io_three/exporter/utilities.py

@@ -74,7 +74,7 @@ def round_off(value, ndigits=ROUND):
     """Round off values to specified limit
 
     :param value: value(s) to round off
-    :param ndigits: limit (Default = ROUND)
+    :param ndigits: limit (Default value = ROUND)
     :type value: float|list|tuple
     :return: the same data type that was passed
     :rtype: float|list|tuple

+ 1 - 1
utils/exporters/blender/addons/io_three/logger.py

@@ -20,7 +20,7 @@ def init(filename, level=constants.DEBUG):
     """Initialize the logger.
 
     :param filename: base name of the log file
-    :param level: logging level (Default = DEBUG)
+    :param level: logging level (Default value = DEBUG)
 
     """
     global LOG_FILE