Просмотр исходного кода

improved handling of material files

DecodeReader() will try the io.Reader first, then the mtllib file
in the OBJ, if present. If all else fails, a default material is
applied. DecodeReader() produces no error for materials.
Ben 6 лет назад
Родитель
Сommit
307c3a42f7
1 измененных файлов с 52 добавлено и 21 удалено
  1. 52 21
      loader/obj/obj.go

+ 52 - 21
loader/obj/obj.go

@@ -99,31 +99,37 @@ func Decode(objpath string, mtlpath string) (*Decoder, error) {
 
 	// If path of material file not supplied,
 	// try to use the base name of the obj file
-	if len(mtlpath) == 0 {
-		dir, objfile := filepath.Split(objpath)
-		ext := filepath.Ext(objfile)
-		mtlpath = dir + objfile[:len(objfile)-len(ext)] + ".mtl"
-	}
+	// if len(mtlpath) == 0 {
+	// 	dir, objfile := filepath.Split(objpath)
+	// 	ext := filepath.Ext(objfile)
+	// 	mtlpath = dir + objfile[:len(objfile)-len(ext)] + ".mtl"
+	// }
 
 	fmt.Println("USING TEST VERSION")
 	// Opens mtl file
+	// if mtlpath=="", then os.Open() will produce an error,
+	// causing fmtl to be nil
 	fmtl, err := os.Open(mtlpath)
 	defer fmtl.Close() // will produce (ignored) err if fmtl==nil
 
-	// fmt.Println("before DecodeReader()")
+	// if fmtl==nil, the io.Reader in DecodeReader() will be (T=*os.File, V=nil)
+	// which is NOT equal to plain nil or (io.Reader, nil)
 	dec, err := DecodeReader(fobj, fmtl)
 	if err != nil {
 		return nil, err
 	}
 
-	// fmt.Println("after DecodeReader()")
-
 	dec.mtlDir = filepath.Dir(objpath)
 	return dec, nil
 }
 
 // DecodeReader decodes the specified obj and mtl readers returning a decoder
-// object and an error.
+// object and an error if a problem was encoutered while parsing the OBJ.
+//
+// Pass a valid io.Reader to override the materials defined in the OBJ file,
+// or `nil` to use the materials listed in the OBJ's "mtllib" line (if present),
+// or a default material as a last resort. No error will be returned for
+// problems with materials--a gray default material will be used.
 func DecodeReader(objreader, mtlreader io.Reader) (*Decoder, error) {
 
 	dec := new(Decoder)
@@ -142,25 +148,50 @@ func DecodeReader(objreader, mtlreader io.Reader) (*Decoder, error) {
 	}
 
 	// fmt.Printf("before dec.parse(mtlreader), mtlreader=<%#v>\n", mtlreader)
+
 	// Parses mtl lines
-	// if parsing causes an error, range over the materials named in the OBJ
-	// file and substitute a default.
+	// 1) try passed in mtlreader,
+	// 2) try file in mtllib line
+	// 3) use default material as last resort
 	dec.matCurrent = nil
 	dec.line = 1
-	// fmt.Println("calling dec.parse(mtlreader)")
+	// first try: use the material file passed in as an io.Reader
 	err = dec.parse(mtlreader, dec.parseMtlLine)
 	if err != nil {
-		// handle error by using default material instead
-		// of simply passing it up the call stack.
-		// But log it.
-		for key := range dec.Materials {
-			dec.Materials[key] = defaultMat
+		// if mtlreader produces an error (eg. it's nil), try the file listed
+		// in the OBJ's matlib line, if it exists.
+		if dec.Matlib != "" {
+			// ... first need to get the path of the OBJ, since mtllib is relative
+			var mtllibPath string
+			if objf, ok := objreader.(*os.File); ok {
+				// NOTE (quillaja): this is a hack because we need the directory of
+				// the OBJ, but can't get it any other way (dec.mtlDir isn't set
+				// until AFTER this function is finished)
+				objdir := filepath.Dir(objf.Name())
+				mtllibPath = filepath.Join(objdir, dec.Matlib)
+			}
+			fmt.Println("mtllib:", mtllibPath)
+			mtlf, errMTL := os.Open(mtllibPath)
+			defer mtlf.Close()
+			if errMTL == nil {
+				fmt.Println("attempting to parse", mtllibPath)
+				err = dec.parse(mtlf, dec.parseMtlLine) // will set err to nil if successful
+				fmt.Println("error while parsing mtllib:", err)
+			}
 		}
-		fmt.Println("logged warning")
-		dec.appendWarn(mtlType, "unable to parse a mtl file for obj. using default material instead.")
-	}
 
-	// fmt.Println("after dec.parse(mtlreader)")
+		// handle error(s) instead of simply passing it up the call stack.
+		// range over the materials named in the OBJ file and substitute a default
+		// But log that an error occured.
+		if err != nil {
+			for key := range dec.Materials {
+				dec.Materials[key] = defaultMat
+			}
+			fmt.Println("logged warning for last ditch effort")
+			// NOTE (quillaja): could be an error instead of some custom type.
+			dec.appendWarn(mtlType, "unable to parse a mtl file for obj. using default material instead.")
+		}
+	}
 
 	return dec, nil
 }