Browse Source

Added handling of morph colors to OBJ converter.

To be used like this:

python convert_obj_three.py -i model.obj -o model.js -m "morphA_*.obj morphB_*.obj" -c "modelA_colors.obj modelB_colors.obj" -s flat -a none
alteredq 14 years ago
parent
commit
df7216e7a6

File diff suppressed because it is too large
+ 1 - 1
examples/obj/female02/Female02_slim.js


File diff suppressed because it is too large
+ 1 - 1
examples/obj/lucy/Lucy100k_slim.js


File diff suppressed because it is too large
+ 1 - 1
examples/obj/male02/Male02_slim.js


File diff suppressed because it is too large
+ 1 - 1
examples/obj/torus/Torus_slim.js


File diff suppressed because it is too large
+ 1 - 1
examples/obj/walt/WaltHead_slim.js


+ 197 - 65
utils/exporters/convert_obj_three.py

@@ -4,7 +4,7 @@
 How to use this converter
 -------------------------
 
-python convert_obj_three.py -i infile.obj -o outfile.js [-m morphfiles*.obj] [-a center|centerxz|top|bottom|none] [-s smooth|flat] [-t ascii|binary] [-d invert|normal]
+python convert_obj_three.py -i infile.obj -o outfile.js [-m morphfiles*.obj] [-c morphcolors*.obj] [-a center|centerxz|top|bottom|none] [-s smooth|flat] [-t ascii|binary] [-d invert|normal]
 
 Notes: 
 
@@ -148,7 +148,7 @@ TEMPLATE_FILE_ASCII = u"""\
 // Converted from: %(fname)s
 //  vertices: %(nvertex)d
 //  faces: %(nface)d 
-//  normals: %(nnormal)d 
+//  normals: %(nnormal)d
 //  uvs: %(nuv)d 
 //  materials: %(nmaterial)d
 //
@@ -163,9 +163,11 @@ var model = {
     "materials": [%(materials)s],
 
     "vertices": [%(vertices)s],
-
+    
     "morphTargets": [%(morphTargets)s],
 
+    "morphColors": [%(morphColors)s],
+
     "normals": [%(normals)s],
 
     "uvs": [[%(uvs)s]],
@@ -204,8 +206,10 @@ TEMPLATE_VERTEX = "%f,%f,%f"
 
 TEMPLATE_N = "%f,%f,%f"
 TEMPLATE_UV = "%f,%f"
+TEMPLATE_COLOR = "%.3f,%.3f,%.3f"
 
-TEMPLATE_MORPH = '\t{ "name": "%s", "vertices": [%s] }'
+TEMPLATE_MORPH_VERTICES = '\t{ "name": "%s", "vertices": [%s] }'
+TEMPLATE_MORPH_COLORS   = '\t{ "name": "%s", "colors": [%s] }'
 
 # #####################################################
 # Utils
@@ -565,7 +569,7 @@ def parse_obj(fname):
     return faces, vertices, uvs, normals, materials, mtllib
     
 # #####################################################
-# Generator
+# Generator - faces
 # #####################################################
 def setBit(value, position, on):
     if on:
@@ -573,10 +577,7 @@ def setBit(value, position, on):
         return (value | mask)
     else:
         mask = ~(1 << position)
-        return (value & mask)
-    
-def generate_vertex(v):
-    return TEMPLATE_VERTEX % (v[0], v[1], v[2])
+        return (value & mask)    
     
 def generate_face(f):
     isTriangle = ( len(f['vertex']) == 3 )
@@ -640,19 +641,143 @@ def generate_face(f):
 
     return ",".join( map(str, faceData) )
 
+# #####################################################
+# Generator - chunks
+# #####################################################
+def generate_vertex(v):
+    return TEMPLATE_VERTEX % (v[0], v[1], v[2])
+
 def generate_normal(n):
     return TEMPLATE_N % (n[0], n[1], n[2])
 
 def generate_uv(uv):
     return TEMPLATE_UV % (uv[0], 1.0 - uv[1])
 
+def generate_color_rgb(c):
+    return TEMPLATE_COLOR % (c[0], c[1], c[2])
+    
 # #####################################################
 # Morphs
 # #####################################################
-def generate_morph(name, vertices):
+def generate_morph_vertex(name, vertices):
     vertex_string = ",".join(generate_vertex(v) for v in vertices)
-    return TEMPLATE_MORPH % (name, vertex_string)
+    return TEMPLATE_MORPH_VERTICES % (name, vertex_string)
+    
+def generate_morph_color(name, colors):
+    color_string = ",".join(generate_color_rgb(c) for c in colors)
+    return TEMPLATE_MORPH_COLORS % (name, color_string)
+
+def extract_face_colors(faces, materials, mtlfilename, basename):
+    
+    # extract diffuse colors from MTL materials
+
+    if not materials:
+        materials = { 'default': 0 }
+
+    mtl = create_materials(materials, mtlfilename, basename)
+    
+    mtl_array = []
+    for m in mtl:
+        if m in materials:
+            index = materials[m]
+            color = mtl[m].get("colorDiffuse", [1,0,0])
+            mtl_array.append([index, color])
+       
+    mtl_array.sort()
+
+    # assign colors to faces
+
+    faceColors = []
+
+    for face in faces:
+        material_index = face['material']
+        faceColors.append(mtl_array[material_index][1])
+
+    return faceColors
+
+def generate_morph_targets(morphfiles, n_vertices, infile):
+    skipOriginalMorph = False
+    norminfile = os.path.normpath(infile)
     
+    morphVertexData = []
+
+    for mfilepattern in morphfiles.split():
+
+        matches = glob.glob(mfilepattern)
+        matches.sort()
+
+        for path in matches:
+
+            normpath = os.path.normpath(path)
+
+            if normpath != norminfile or not skipOriginalMorph:
+
+                name = os.path.basename(normpath)
+                
+                morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
+                
+                n_morph_vertices = len(morphVertices)
+
+                if n_vertices != n_morph_vertices:
+
+                    print "WARNING: skipping morph [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices)
+
+                else:
+                    
+                    if ALIGN == "center":
+                        center(morphVertices)
+                    elif ALIGN == "centerxz":
+                        centerxz(morphVertices)
+                    elif ALIGN == "bottom":
+                        bottom(morphVertices)
+                    elif ALIGN == "top":
+                        top(morphVertices)
+                        
+                    morphVertexData.append((get_name(name), morphVertices))
+                    print "adding [%s] with %d vertices" % (name, n_morph_vertices)
+    
+    morphTargets = ""
+    if len(morphVertexData):
+        morphTargets = "\n%s\n\t" % ",\n".join(generate_morph_vertex(name, vertices) for name, vertices in morphVertexData)
+
+    return morphTargets
+    
+def generate_morph_colors(colorfiles, n_vertices, n_faces):
+    morphColorData = []
+
+    for mfilepattern in colorfiles.split():
+
+        matches = glob.glob(mfilepattern)
+        matches.sort()
+        for path in matches:
+            normpath = os.path.normpath(path)
+            name = os.path.basename(normpath)
+
+            morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
+
+            n_morph_vertices = len(morphVertices)
+            n_morph_faces = len(morphFaces)
+
+            if n_vertices != n_morph_vertices:
+
+                print "WARNING: skipping morph color map [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices)
+
+            elif n_faces != n_morph_faces:
+
+                print "WARNING: skipping morph color map [%s] with different number of faces [%d] than the original model [%d]" % (name, n_morph_faces, n_faces)
+
+            else:
+
+                morphFaceColors = extract_face_colors(morphFaces, morphMaterials, morphMtllib, normpath)
+                morphColorData.append((get_name(name), morphFaceColors))
+                print "adding [%s] with %d face colors" % (name, len(morphFaceColors))
+
+    morphColors = ""
+    if len(morphColorData):
+        morphColors = "\n%s\n\t" % ",\n".join(generate_morph_color(name, colors) for name, colors in morphColorData)
+    
+    return morphColors
+
 # #####################################################
 # Materials
 # #####################################################
@@ -717,34 +842,49 @@ def generate_mtl(materials):
         }
     return mtl
     
-def generate_materials_string(materials, mtllib):
+def generate_materials_string(materials, mtlfilename, basename):
     """Generate final materials string.
     """
 
-    random.seed(42) # to get well defined color order for materials
+    if not materials:
+        materials = { 'default': 0 }
+
+    mtl = create_materials(materials, mtlfilename, basename)
+    return generate_materials(mtl, materials)
+    
+def create_materials(materials, mtlfilename, basename):
+    """Parse MTL file and create mapping between its materials and OBJ materials.
+       Eventual edge cases are handled here (missing materials, missing MTL file).
+    """
+
+    random.seed(42) # to get well defined color order for debug colors
     
     # default materials with debug colors for when
     # there is no specified MTL / MTL loading failed,
     # or if there were no materials / null materials
-    if not materials:
-        materials = { 'default':0 }
+
     mtl = generate_mtl(materials)
     
-    if mtllib:
+    if mtlfilename:
+
         # create full pathname for MTL (included from OBJ)
-        path = os.path.dirname(infile)
-        fname = os.path.join(path, mtllib)
+
+        path = os.path.dirname(basename)
+        fname = os.path.join(path, mtlfilename)
         
         if file_exists(fname):
+
             # override default materials with real ones from MTL
             # (where they exist, otherwise keep defaults)
+
             mtl.update(parse_mtl(fname))
         
         else:
+
             print "Couldn't find [%s]" % fname
-    
-    return generate_materials(mtl, materials)
-    
+            
+    return mtl
+
 # #####################################################
 # Faces
 # #####################################################
@@ -809,7 +949,7 @@ def sort_faces(faces):
 # #####################################################
 # API - ASCII converter
 # #####################################################
-def convert_ascii(infile, morphfiles, outfile):
+def convert_ascii(infile, morphfiles, colorfiles, outfile):
     """Convert infile.obj to outfile.js
     
     Here is where everything happens. If you need to automate conversions,
@@ -819,9 +959,16 @@ def convert_ascii(infile, morphfiles, outfile):
     if not file_exists(infile):
         print "Couldn't find [%s]" % infile
         return
-        
+       
+    # parse OBJ / MTL files
+
     faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
-    
+
+    n_vertices = len(vertices)
+    n_faces = len(faces)
+
+    # align model
+
     if ALIGN == "center":
         center(vertices)
     elif ALIGN == "centerxz":
@@ -831,48 +978,24 @@ def convert_ascii(infile, morphfiles, outfile):
     elif ALIGN == "top":
         top(vertices)
     
-    n_vertices = len(vertices)
-    
+    # generate normals string
+
     nnormal = 0
     normals_string = ""
     if SHADING == "smooth":
         normals_string = ",".join(generate_normal(n) for n in normals)
         nnormal = len(normals)
     
-    skipOriginalMorph = False
-    norminfile = os.path.normpath(infile)
+    # extract morph vertices
     
-    morphData = []
-    for mfilepattern in morphfiles.split():
-        matches = glob.glob(mfilepattern)
-        matches.sort()
-        for path in matches:
-            normpath = os.path.normpath(path)
-            if normpath != norminfile or not skipOriginalMorph:
-                name = os.path.basename(normpath)
-                
-                morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
-                
-                n_morph_vertices = len(morphVertices)
-                if n_vertices != n_morph_vertices:
-                    print "WARNING: skipping morph [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices)
-                else:                    
-                    if ALIGN == "center":
-                        center(morphVertices)
-                    elif ALIGN == "centerxz":
-                        centerxz(morphVertices)
-                    elif ALIGN == "bottom":
-                        bottom(morphVertices)
-                    elif ALIGN == "top":
-                        top(morphVertices)
-                        
-                    morphData.append((get_name(name), morphVertices ))
-                    print "adding [%s] with %d vertices" % (name, len(morphVertices))
+    morphTargets = generate_morph_targets(morphfiles, n_vertices, infile)
     
-    morphTargets = ""
-    if len(morphData):
-        morphTargets = "\n%s\n\t" % ",\n".join(generate_morph(name, vertices) for name, vertices in morphData)
+    # extract morph colors
+
+    morphColors = generate_morph_colors(colorfiles, n_vertices, n_faces)
     
+    # generate ascii model string
+
     text = TEMPLATE_FILE_ASCII % {
     "name"      : get_name(outfile),
     "fname"     : infile,
@@ -882,13 +1005,14 @@ def convert_ascii(infile, morphfiles, outfile):
     "nnormal"   : nnormal,
     "nmaterial" : len(materials),
 
-    "materials" : generate_materials_string(materials, mtllib),
+    "materials" : generate_materials_string(materials, mtllib, infile),
 
     "normals"       : normals_string,
     "uvs"           : ",".join(generate_uv(uv) for uv in uvs),
     "vertices"      : ",".join(generate_vertex(v) for v in vertices),
     
     "morphTargets"  : morphTargets,
+    "morphColors"   : morphColors,
     
     "faces"     : ",".join(generate_face(f) for f in faces)
     }
@@ -898,7 +1022,7 @@ def convert_ascii(infile, morphfiles, outfile):
     out.close()
     
     print "%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials))
-        
+
     
 # #############################################################################
 # API - Binary converter
@@ -933,7 +1057,7 @@ def convert_binary(infile, outfile):
     text = TEMPLATE_FILE_BIN % {
     "name"       : get_name(outfile),
     
-    "materials" : generate_materials_string(materials, mtllib),
+    "materials" : generate_materials_string(materials, mtllib, infile),
     "buffers"   : binfile,
     
     "fname"     : infile,
@@ -1216,7 +1340,7 @@ def convert_binary(infile, outfile):
 # Helpers
 # #############################################################################
 def usage():
-    print "Usage: %s -i filename.obj -o filename.js [-m morphfiles*.obj] [-a center|top|bottom] [-s flat|smooth] [-t binary|ascii] [-d invert|normal]" % os.path.basename(sys.argv[0])
+    print "Usage: %s -i filename.obj -o filename.js [-m morphfiles*.obj] [-c morphcolors*.obj] [-a center|top|bottom] [-s flat|smooth] [-t binary|ascii] [-d invert|normal]" % os.path.basename(sys.argv[0])
         
 # #####################################################
 # Main
@@ -1225,7 +1349,7 @@ if __name__ == "__main__":
     
     # get parameters from the command line
     try:
-        opts, args = getopt.getopt(sys.argv[1:], "hi:m:o:a:s:t:d:", ["help", "input=", "morphs=", "output=", "align=", "shading=", "type=", "dissolve="])
+        opts, args = getopt.getopt(sys.argv[1:], "hi:m:c:o:a:s:t:d:", ["help", "input=", "morphs=", "colors=", "output=", "align=", "shading=", "type=", "dissolve="])
     
     except getopt.GetoptError:
         usage()
@@ -1233,6 +1357,7 @@ if __name__ == "__main__":
         
     infile = outfile = ""
     morphfiles = ""
+    colorfiles = ""
     
     for o, a in opts:
         if o in ("-h", "--help"):
@@ -1245,6 +1370,9 @@ if __name__ == "__main__":
         elif o in ("-m", "--morphs"):
             morphfiles = a
 
+        elif o in ("-c", "--colors"):
+            colorfiles = a
+
         elif o in ("-o", "--output"):
             outfile = a
 
@@ -1269,11 +1397,15 @@ if __name__ == "__main__":
         sys.exit(2)
     
     print "Converting [%s] into [%s] ..." % (infile, outfile)
+
     if morphfiles:
-        print "Morphs [%s]" % morphfiles    
-    
+        print "Morphs [%s]" % morphfiles
+
+    if colorfiles:
+        print "Colors [%s]" % colorfiles
+
     if TYPE == "ascii":
-        convert_ascii(infile, morphfiles, outfile)
+        convert_ascii(infile, morphfiles, colorfiles, outfile)
     elif TYPE == "binary":
         convert_binary(infile, outfile)
     

Some files were not shown because too many files changed in this diff