Browse Source

Merge pull request #11836 from manthrax/blender-exporter-multimaterial

Blender exporter multimaterial
Mr.doob 8 years ago
parent
commit
bc50e4971f

+ 3 - 1
utils/exporters/blender/addons/io_three/__init__.py

@@ -111,7 +111,7 @@ def _blending_types(index):
 bpy.types.Material.THREE_blending_type = EnumProperty(
 bpy.types.Material.THREE_blending_type = EnumProperty(
     name="Blending type",
     name="Blending type",
     description="Blending type",
     description="Blending type",
-    items=[_blending_types(x) for x in range(5)],
+    items=[_blending_types(x) for x in range(6)],
     default=constants.BLENDING_TYPES.NORMAL)
     default=constants.BLENDING_TYPES.NORMAL)
 
 
 bpy.types.Material.THREE_depth_write = BoolProperty(default=True)
 bpy.types.Material.THREE_depth_write = BoolProperty(default=True)
@@ -769,6 +769,7 @@ class ExportThree(bpy.types.Operator, ExportHelper):
         default=2)
         default=2)
 
 
     def invoke(self, context, event):
     def invoke(self, context, event):
+        
         settings = context.scene.get(constants.EXPORT_SETTINGS_KEY)
         settings = context.scene.get(constants.EXPORT_SETTINGS_KEY)
         if settings:
         if settings:
             try:
             try:
@@ -797,6 +798,7 @@ class ExportThree(bpy.types.Operator, ExportHelper):
         :param context:
         :param context:
 
 
         """
         """
+
         if not self.properties.filepath:
         if not self.properties.filepath:
             raise Exception("filename not set")
             raise Exception("filename not set")
 
 

+ 12 - 0
utils/exporters/blender/addons/io_three/constants.py

@@ -13,6 +13,15 @@ BLENDING_TYPES = type('Blending', (), {
     'CUSTOM': 'CustomBlending'
     'CUSTOM': 'CustomBlending'
 })
 })
 
 
+BLENDING_CONSTANTS = type('BlendingConstant', (), {
+    'NoBlending':0,
+    'NormalBlending':1,
+    'AdditiveBlending':2,
+    'SubtractiveBlending':3,
+    'MultiplyBlending':4,
+    'CustomBlending':5
+})
+
 NEAREST_FILTERS = type('NearestFilters', (), {
 NEAREST_FILTERS = type('NearestFilters', (), {
     'NEAREST': 'NearestFilter',
     'NEAREST': 'NearestFilter',
     'MIP_MAP_NEAREST': 'NearestMipMapNearestFilter',
     'MIP_MAP_NEAREST': 'NearestMipMapNearestFilter',
@@ -73,6 +82,9 @@ DRAW_CALLS = 'drawcalls'
 DC_START = 'start'
 DC_START = 'start'
 DC_COUNT = 'count'
 DC_COUNT = 'count'
 DC_INDEX = 'index'
 DC_INDEX = 'index'
+
+GROUPS = 'groups'
+
 SCALE = 'scale'
 SCALE = 'scale'
 COMPRESSION = 'compression'
 COMPRESSION = 'compression'
 MAPS = 'maps'
 MAPS = 'maps'

+ 2 - 0
utils/exporters/blender/addons/io_three/exporter/api/material.py

@@ -44,6 +44,8 @@ def blending(material):
     except AttributeError:
     except AttributeError:
         logger.debug("No THREE_blending_type attribute found")
         logger.debug("No THREE_blending_type attribute found")
         blend = constants.NORMAL_BLENDING
         blend = constants.NORMAL_BLENDING
+
+    blend = getattr( constants.BLENDING_CONSTANTS , blend) #manthrax: Translate the blending type name, to the three.js constant value.
     return blend
     return blend
 
 
 
 

+ 40 - 7
utils/exporters/blender/addons/io_three/exporter/api/mesh.py

@@ -204,6 +204,24 @@ def buffer_normal(mesh, options):
     return normals_
     return normals_
 
 
 
 
+@_mesh
+def buffer_face_material(mesh, options):
+    """
+
+    :param mesh:
+    :rtype: []
+
+    """
+    face_material = []
+    logger.info("Retrieving face materials.")
+
+    for face in mesh.tessfaces:
+        #logger.info("face:%d,%d",face.index,face.material_index)
+        face_material.append(face.material_index)
+
+    return face_material
+
+
 @_mesh
 @_mesh
 def buffer_position(mesh, options):
 def buffer_position(mesh, options):
     """
     """
@@ -692,9 +710,15 @@ def materials(mesh, options):
         return []
         return []
 
 
     indices = []
     indices = []
-    for face in mesh.tessfaces:
-        if face.material_index not in indices:
-            indices.append(face.material_index)
+    
+    #manthrax: Disable the following logic that attempts to find only the used materials on this mesh
+    #for face in mesh.tessfaces:
+    #    if face.material_index not in indices:
+    #        indices.append(face.material_index)
+    # instead, export all materials on this object... they are probably there for a good reason, even if they aren't referenced by the geometry at present...
+    for index in range(len( mesh.materials )):
+        indices.append(index)
+
 
 
     material_sets = [(mesh.materials[index], index) for index in indices]
     material_sets = [(mesh.materials[index], index) for index in indices]
     materials_ = []
     materials_ = []
@@ -1198,10 +1222,12 @@ def _armature(mesh):
     """
     """
     obj = object_.objects_using_mesh(mesh)[0]
     obj = object_.objects_using_mesh(mesh)[0]
     armature = obj.find_armature()
     armature = obj.find_armature()
-    if armature:
-        logger.info("Found armature %s for %s", armature.name, obj.name)
-    else:
-        logger.info("Found no armature for %s", obj.name)
+    
+    #manthrax: Remove logging spam. This was spamming on every vertex...
+    #if armature:
+    #    logger.info("Found armature %s for %s", armature.name, obj.name)
+    #else:
+    #    logger.info("Found no armature for %s", obj.name)
     return armature
     return armature
 
 
 
 
@@ -1296,6 +1322,7 @@ def _pose_bones(armature):
         bones_.append({
         bones_.append({
             constants.PARENT: bone_index,
             constants.PARENT: bone_index,
             constants.NAME: armature_bone.name,
             constants.NAME: armature_bone.name,
+
             constants.POS: (pos.x, pos.z, -pos.y),
             constants.POS: (pos.x, pos.z, -pos.y),
             constants.ROTQ: (rot.x, rot.z, -rot.y, rot.w),
             constants.ROTQ: (rot.x, rot.z, -rot.y, rot.w),
             constants.SCL: (scl.x, scl.z, scl.y)
             constants.SCL: (scl.x, scl.z, scl.y)
@@ -1326,9 +1353,12 @@ def _rest_bones(armature):
 
 
         if bone.parent is None:
         if bone.parent is None:
             bone_pos = bone.head_local
             bone_pos = bone.head_local
+            logger.debug("Root bone:%s",str(bone_pos))
             bone_index = -1
             bone_index = -1
         else:
         else:
             bone_pos = bone.head_local - bone.parent.head_local
             bone_pos = bone.head_local - bone.parent.head_local
+            logger.debug("Child bone:%s",str(bone_pos))
+
             bone_index = 0
             bone_index = 0
             index = 0
             index = 0
             for parent in armature.data.bones:
             for parent in armature.data.bones:
@@ -1337,10 +1367,13 @@ def _rest_bones(armature):
                 index += 1
                 index += 1
 
 
         bone_world_pos = armature.matrix_world * bone_pos
         bone_world_pos = armature.matrix_world * bone_pos
+
         x_axis = bone_world_pos.x
         x_axis = bone_world_pos.x
         y_axis = bone_world_pos.z
         y_axis = bone_world_pos.z
         z_axis = -bone_world_pos.y
         z_axis = -bone_world_pos.y
 
 
+        logger.debug("Bone pos:%s",str(bone_world_pos))
+
         logger.debug("Adding bone %s at: %s, %s",
         logger.debug("Adding bone %s at: %s, %s",
                      bone.name, bone_index, bone_index_rel)
                      bone.name, bone_index, bone_index_rel)
         bone_map[bone_count] = bone_index_rel
         bone_map[bone_count] = bone_index_rel

+ 3 - 2
utils/exporters/blender/addons/io_three/exporter/api/object.py

@@ -371,7 +371,6 @@ def receive_shadow(obj):
                     return True
                     return True
         return False
         return False
 
 
-# manthrax: TODO: Would like to evaluate wether this axis conversion stuff is still neccesary AXIS_CONVERSION = mathutils.Matrix()
 AXIS_CONVERSION = axis_conversion(to_forward='Z', to_up='Y').to_4x4() 
 AXIS_CONVERSION = axis_conversion(to_forward='Z', to_up='Y').to_4x4() 
 
 
 @_object
 @_object
@@ -498,6 +497,7 @@ def extract_mesh(obj, options, recalculate=False):
     obj.data = mesh_node
     obj.data = mesh_node
     obj.select = True
     obj.select = True
 
 
+    logger.info("Applying EDGE_SPLIT modifier....")
     bpy.ops.object.modifier_add(type='EDGE_SPLIT')
     bpy.ops.object.modifier_add(type='EDGE_SPLIT')
     bpy.context.object.modifiers['EdgeSplit'].use_edge_angle = False
     bpy.context.object.modifiers['EdgeSplit'].use_edge_angle = False
     bpy.context.object.modifiers['EdgeSplit'].use_edge_sharp = True
     bpy.context.object.modifiers['EdgeSplit'].use_edge_sharp = True
@@ -594,7 +594,8 @@ def objects_using_mesh(mesh_node):
     :return: list of object names
     :return: list of object names
 
 
     """
     """
-    logger.debug('object.objects_using_mesh(%s)', mesh_node)
+    #manthrax: remove spam
+    #logger.debug('object.objects_using_mesh(%s)', mesh_node)
     for mesh_name, objects in _MESH_MAP.items():
     for mesh_name, objects in _MESH_MAP.items():
         if mesh_name == mesh_node.name:
         if mesh_name == mesh_node.name:
             return objects
             return objects

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

@@ -238,7 +238,8 @@ class Geometry(base_classes.BaseNode):
             index = self.get(constants.INDEX)
             index = self.get(constants.INDEX)
             if index is not None:
             if index is not None:
                 data[constants.INDEX] = index
                 data[constants.INDEX] = index
-            data[constants.ATTRIBUTES] = self.get(constants.ATTRIBUTES)
+            data[constants.ATTRIBUTES] = self.get(constants.ATTRIBUTES)           
+            data[constants.GROUPS] = self.get(constants.GROUPS)
             return {constants.DATA: data}
             return {constants.DATA: data}
 
 
         components = [constants.VERTICES, constants.FACES,
         components = [constants.VERTICES, constants.FACES,
@@ -445,6 +446,8 @@ class Geometry(base_classes.BaseNode):
             assert(len(attrib_data_in) > 0)
             assert(len(attrib_data_in) > 0)
             array, item_size = attrib_data_in[0]
             array, item_size = attrib_data_in[0]
             i, n = 0, len(array) / item_size
             i, n = 0, len(array) / item_size
+
+
             while i < n:
             while i < n:
 
 
                 vertex_data = ()
                 vertex_data = ()
@@ -486,6 +489,60 @@ class Geometry(base_classes.BaseNode):
                     indexed.clear()
                     indexed.clear()
                     flush_req = False
                     flush_req = False
 
 
+
+            #manthrax: Adding group support for multiple materials
+            #index_threshold = indices_per_face*100
+            face_materials = api.mesh.buffer_face_material(self.node,self.options)
+            logger.info("Face material list length:%d",len(face_materials))
+            logger.info("Drawcall parameters count:%s item_size=%s",n,item_size)
+            assert((len(face_materials)*3)==n)
+            #Re-index the index buffer by material
+            used_material_indexes = {}
+            #Get lists of faces indices per material
+            for idx, mat_index in enumerate(face_materials):
+                if used_material_indexes.get(mat_index) is None:
+                    used_material_indexes[mat_index] = [idx]
+                else:
+                    used_material_indexes[mat_index].append(idx)
+
+            logger.info("# Faces by material:%s",str(used_material_indexes))
+
+            #manthrax: build new index list from lists of faces by material, and build the draw groups at the same time...
+            groups = []
+            new_index = []
+            print("Mat index:",str(used_material_indexes))
+
+            for mat_index in used_material_indexes:
+                face_array=used_material_indexes[mat_index]
+                print("Mat index:",str(mat_index),str(face_array))
+
+                print( dir(self.node) )
+
+                group = {
+                    'start': len(new_index),
+                    'count': len(face_array)*3,
+                    'materialIndex': mat_index
+                }
+                groups.append(group)
+
+                for fi in range(len(face_array)):
+                    prim_index = face_array[fi]
+                    prim_index = prim_index * 3
+                    new_index.extend([index_data[prim_index],index_data[prim_index+1],index_data[prim_index+2]])
+
+            if len(groups) > 0:
+                index_data = new_index
+                self[constants.GROUPS]=groups
+            #else:
+            #    self[constants.GROUPS]=[{
+            #    'start':0,
+            #    'count':n,
+            #   'materialIndex':0
+            #}]
+            #manthrax: End group support
+
+
+
             for i, key in enumerate(attrib_keys):
             for i, key in enumerate(attrib_keys):
                 array = attrib_data_out[i][0]
                 array = attrib_data_out[i][0]
                 self[constants.ATTRIBUTES][key][constants.ARRAY] = array
                 self[constants.ATTRIBUTES][key][constants.ARRAY] = array

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

@@ -94,7 +94,9 @@ class Object(base_classes.BaseNode):
 
 
             material_names = api.object.material(self.node) #manthrax: changes for multimaterial start here
             material_names = api.object.material(self.node) #manthrax: changes for multimaterial start here
             if material_names:
             if material_names:
-                logger.info("Got material names for this object:",material_names);
+
+                logger.info("Got material names for this object:%s",str(material_names));
+
                 materialArray = [self.scene.material(objname)[constants.UUID] for objname in material_names]
                 materialArray = [self.scene.material(objname)[constants.UUID] for objname in material_names]
                 if len(materialArray) == 0:  # If no materials.. dont export a material entry
                 if len(materialArray) == 0:  # If no materials.. dont export a material entry
                     materialArray = None
                     materialArray = None
@@ -102,7 +104,8 @@ class Object(base_classes.BaseNode):
                     materialArray = materialArray[0]
                     materialArray = materialArray[0]
                 # else export array of material uuids
                 # else export array of material uuids
                 self[constants.MATERIAL] = materialArray
                 self[constants.MATERIAL] = materialArray
-                logger.info("materials:",self[constants.MATERIAL]);
+
+                logger.info("Materials:%s",str(self[constants.MATERIAL]));
             else:
             else:
                 logger.info("%s has no materials", self.node) #manthrax: end multimaterial
                 logger.info("%s has no materials", self.node) #manthrax: end multimaterial