obj.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package obj is used to parse the Wavefront OBJ file format (*.obj), including
  5. // associated materials (*.mtl). Not all features of the OBJ format are
  6. // supported. Basic format info: https://en.wikipedia.org/wiki/Wavefront_.obj_file
  7. package obj
  8. import (
  9. "bufio"
  10. "errors"
  11. "fmt"
  12. "io"
  13. "math"
  14. "os"
  15. "path/filepath"
  16. "strconv"
  17. "strings"
  18. "github.com/g3n/engine/core"
  19. "github.com/g3n/engine/geometry"
  20. "github.com/g3n/engine/gls"
  21. "github.com/g3n/engine/graphic"
  22. "github.com/g3n/engine/material"
  23. "github.com/g3n/engine/math32"
  24. "github.com/g3n/engine/texture"
  25. )
  26. // Decoder contains all decoded data from the obj and mtl files
  27. type Decoder struct {
  28. Objects []Object // decoded objects
  29. Matlib string // name of the material lib
  30. Materials map[string]*Material // maps material name to object
  31. Vertices math32.ArrayF32 // vertices positions array
  32. Normals math32.ArrayF32 // vertices normals
  33. Uvs math32.ArrayF32 // vertices texture coordinates
  34. Warnings []string // warning messages
  35. line uint // current line number
  36. objCurrent *Object // current object
  37. matCurrent *Material // current material
  38. smoothCurrent bool // current smooth state
  39. mtlDir string // Directory of material file
  40. }
  41. // Object contains all information about one decoded object
  42. type Object struct {
  43. Name string // Object name
  44. Faces []Face // Faces
  45. materials []string // Materials used in this object
  46. }
  47. // Face contains all information about an object face
  48. type Face struct {
  49. Vertices []int // Indices to the face vertices
  50. Uvs []int // Indices to the face UV coordinates
  51. Normals []int // Indices to the face normals
  52. Material string // Material name
  53. Smooth bool // Smooth face
  54. }
  55. // Material contains all information about an object material
  56. type Material struct {
  57. Name string // Material name
  58. Illum int // Illumination model
  59. Opacity float32 // Opacity factor
  60. Refraction float32 // Refraction factor
  61. Shininess float32 // Shininess (specular exponent)
  62. Ambient math32.Color // Ambient color reflectivity
  63. Diffuse math32.Color // Diffuse color reflectivity
  64. Specular math32.Color // Specular color reflectivity
  65. Emissive math32.Color // Emissive color
  66. MapKd string // Texture file linked to diffuse color
  67. }
  68. // Light gray default material used as when other materials cannot be loaded.
  69. var defaultMat = &Material{
  70. Diffuse: math32.Color{R: 0.7, G: 0.7, B: 0.7},
  71. Ambient: math32.Color{R: 0.7, G: 0.7, B: 0.7},
  72. Specular: math32.Color{R: 0.5, G: 0.5, B: 0.5},
  73. Shininess: 30.0,
  74. }
  75. // Local constants
  76. const (
  77. blanks = "\r\n\t "
  78. invINDEX = math.MaxUint32
  79. objType = "obj"
  80. mtlType = "mtl"
  81. )
  82. // Decode decodes the specified obj and mtl files returning a decoder
  83. // object and an error. Passing an empty string (or otherwise invalid path)
  84. // to mtlpath will cause the decoder to check the 'mtllib' file in the OBJ if
  85. // present, and fall back to a default material as a last resort.
  86. func Decode(objpath string, mtlpath string) (*Decoder, error) {
  87. // Opens obj file
  88. fobj, err := os.Open(objpath)
  89. if err != nil {
  90. return nil, err
  91. }
  92. defer fobj.Close()
  93. // TODO: remove
  94. // If path of material file not supplied,
  95. // try to use the base name of the obj file
  96. // if len(mtlpath) == 0 {
  97. // dir, objfile := filepath.Split(objpath)
  98. // ext := filepath.Ext(objfile)
  99. // mtlpath = dir + objfile[:len(objfile)-len(ext)] + ".mtl"
  100. // }
  101. fmt.Println("USING TEST VERSION") // TODO: remove
  102. // Opens mtl file
  103. // if mtlpath=="", then os.Open() will produce an error,
  104. // causing fmtl to be nil
  105. fmtl, err := os.Open(mtlpath)
  106. defer fmtl.Close() // will produce (ignored) err if fmtl==nil
  107. // if fmtl==nil, the io.Reader in DecodeReader() will be (T=*os.File, V=nil)
  108. // which is NOT equal to plain nil or (io.Reader, nil) but will produce
  109. // the desired result of passing nil to DecodeReader() per it's func comment.
  110. dec, err := DecodeReader(fobj, fmtl)
  111. if err != nil {
  112. return nil, err
  113. }
  114. dec.mtlDir = filepath.Dir(objpath)
  115. return dec, nil
  116. }
  117. // DecodeReader decodes the specified obj and mtl readers returning a decoder
  118. // object and an error if a problem was encoutered while parsing the OBJ.
  119. //
  120. // Pass a valid io.Reader to override the materials defined in the OBJ file,
  121. // or `nil` to use the materials listed in the OBJ's "mtllib" line (if present),
  122. // a ".mtl" file with the same name as the OBJ file if presemt, or a default
  123. // material as a last resort. No error will be returned for problems
  124. // with materials--a gray default material will be used if nothing else works.
  125. func DecodeReader(objreader, mtlreader io.Reader) (*Decoder, error) {
  126. dec := new(Decoder)
  127. dec.Objects = make([]Object, 0)
  128. dec.Warnings = make([]string, 0)
  129. dec.Materials = make(map[string]*Material)
  130. dec.Vertices = math32.NewArrayF32(0, 0)
  131. dec.Normals = math32.NewArrayF32(0, 0)
  132. dec.Uvs = math32.NewArrayF32(0, 0)
  133. dec.line = 1
  134. // Parses obj lines
  135. err := dec.parse(objreader, dec.parseObjLine)
  136. if err != nil {
  137. return nil, err
  138. }
  139. // Parses mtl lines
  140. // 1) try passed in mtlreader,
  141. // 2) try file in mtllib line
  142. // 3) try <obj_filename>.mtl
  143. // 4) use default material as last resort
  144. dec.matCurrent = nil
  145. dec.line = 1
  146. // first try: use the material file passed in as an io.Reader
  147. err = dec.parse(mtlreader, dec.parseMtlLine)
  148. if err != nil {
  149. // 2) if mtlreader produces an error (eg. it's nil), try the file listed
  150. // in the OBJ's matlib line, if it exists.
  151. if dec.Matlib != "" {
  152. // ... first need to get the path of the OBJ, since mtllib is relative
  153. var mtllibPath string
  154. if objf, ok := objreader.(*os.File); ok {
  155. // NOTE (quillaja): this is a hack because we need the directory of
  156. // the OBJ, but can't get it any other way (dec.mtlDir isn't set
  157. // until AFTER this function is finished).
  158. objdir := filepath.Dir(objf.Name())
  159. mtllibPath = filepath.Join(objdir, dec.Matlib)
  160. dec.mtlDir = objdir // NOTE (quillaja): should this be set?
  161. }
  162. fmt.Println("mtllib:", mtllibPath) // TODO: remove
  163. mtlf, errMTL := os.Open(mtllibPath)
  164. defer mtlf.Close()
  165. if errMTL == nil {
  166. fmt.Println("attempting to parse", mtllibPath) // TODO: remove
  167. err = dec.parse(mtlf, dec.parseMtlLine) // will set err to nil if successful
  168. fmt.Println("error while parsing mtllib:", err) // TODO: remove
  169. }
  170. }
  171. // 3) if the mtllib line fails try <obj_filename>.mtl in the same directory.
  172. // process is basically identical to the above code block.
  173. if err != nil {
  174. var mtlpath string
  175. if objf, ok := objreader.(*os.File); ok {
  176. objdir := strings.TrimSuffix(objf.Name(), ".obj")
  177. mtlpath = objdir + ".mtl"
  178. dec.mtlDir = objdir // NOTE (quillaja): should this be set?
  179. }
  180. fmt.Println("mtl file:", mtlpath) // TODO: remove
  181. mtlf, errMTL := os.Open(mtlpath)
  182. defer mtlf.Close()
  183. if errMTL == nil {
  184. fmt.Println("attempting to parse", mtlpath) // TODO: remove
  185. err = dec.parse(mtlf, dec.parseMtlLine) // will set err to nil if successful
  186. fmt.Println("error while parsing <obj>.mtl:", err) // TODO: remove
  187. if err == nil {
  188. // log a warning
  189. msg := fmt.Sprintf("using material file %s", mtlpath)
  190. dec.appendWarn(mtlType, msg)
  191. }
  192. }
  193. }
  194. // 4) handle error(s) instead of simply passing it up the call stack.
  195. // range over the materials named in the OBJ file and substitute a default
  196. // But log that an error occured.
  197. if err != nil {
  198. for key := range dec.Materials {
  199. dec.Materials[key] = defaultMat
  200. }
  201. fmt.Println("logged warning for last ditch effort") // TODO: remove
  202. // NOTE (quillaja): could be an error of some custom type. But people
  203. // tend to ignore errors and pass them up the call stack instead
  204. // of handling them... so all this work would probably be wasted.
  205. dec.appendWarn(mtlType, "unable to parse a material file for obj. using default material instead.")
  206. }
  207. }
  208. return dec, nil
  209. }
  210. // NewGroup creates and returns a group containing as children meshes
  211. // with all the decoded objects.
  212. // A group is returned even if there is only one object decoded.
  213. func (dec *Decoder) NewGroup() (*core.Node, error) {
  214. group := core.NewNode()
  215. for i := 0; i < len(dec.Objects); i++ {
  216. mesh, err := dec.NewMesh(&dec.Objects[i])
  217. if err != nil {
  218. return nil, err
  219. }
  220. group.Add(mesh)
  221. }
  222. return group, nil
  223. }
  224. // NewMesh creates and returns a mesh from an specified decoded object.
  225. func (dec *Decoder) NewMesh(obj *Object) (*graphic.Mesh, error) {
  226. // Creates object geometry
  227. geom, err := dec.NewGeometry(obj)
  228. if err != nil {
  229. return nil, err
  230. }
  231. // Single material
  232. if geom.GroupCount() == 1 {
  233. // get Material info from mtl file and ensure it's valid.
  234. // substitute default material if it is not.
  235. var matDesc *Material
  236. var matName string
  237. if len(obj.materials) > 0 {
  238. matName = obj.materials[0]
  239. }
  240. matDesc = dec.Materials[matName]
  241. if matDesc == nil {
  242. matDesc = defaultMat
  243. // log warning
  244. msg := fmt.Sprintf("could not find material for %s. using default material.", obj.Name)
  245. dec.appendWarn(objType, msg)
  246. }
  247. // Creates material for mesh
  248. mat := material.NewPhong(&matDesc.Diffuse)
  249. ambientColor := mat.AmbientColor()
  250. mat.SetAmbientColor(ambientColor.Multiply(&matDesc.Ambient))
  251. mat.SetSpecularColor(&matDesc.Specular)
  252. mat.SetShininess(matDesc.Shininess)
  253. // Loads material textures if specified
  254. err = dec.loadTex(&mat.Material, matDesc)
  255. if err != nil {
  256. return nil, err
  257. }
  258. return graphic.NewMesh(geom, mat), nil
  259. }
  260. // Multi material
  261. mesh := graphic.NewMesh(geom, nil)
  262. for idx := 0; idx < geom.GroupCount(); idx++ {
  263. group := geom.GroupAt(idx)
  264. // get Material info from mtl file and ensure it's valid.
  265. // substitute default material if it is not.
  266. var matDesc *Material
  267. var matName string
  268. if len(obj.materials) > group.Matindex {
  269. matName = obj.materials[group.Matindex]
  270. }
  271. matDesc = dec.Materials[matName]
  272. if matDesc == nil {
  273. matDesc = defaultMat
  274. // log warning
  275. msg := fmt.Sprintf("could not find material for %s. using default material.", obj.Name)
  276. dec.appendWarn(objType, msg)
  277. }
  278. // Creates material for mesh
  279. matGroup := material.NewPhong(&matDesc.Diffuse)
  280. ambientColor := matGroup.AmbientColor()
  281. matGroup.SetAmbientColor(ambientColor.Multiply(&matDesc.Ambient))
  282. matGroup.SetSpecularColor(&matDesc.Specular)
  283. matGroup.SetShininess(matDesc.Shininess)
  284. // Loads material textures if specified
  285. err = dec.loadTex(&matGroup.Material, matDesc)
  286. if err != nil {
  287. return nil, err
  288. }
  289. mesh.AddGroupMaterial(matGroup, idx)
  290. }
  291. return mesh, nil
  292. }
  293. // NewGeometry generates and returns a geometry from the specified object
  294. func (dec *Decoder) NewGeometry(obj *Object) (*geometry.Geometry, error) {
  295. geom := geometry.NewGeometry()
  296. // Create buffers
  297. positions := math32.NewArrayF32(0, 0)
  298. normals := math32.NewArrayF32(0, 0)
  299. uvs := math32.NewArrayF32(0, 0)
  300. indices := math32.NewArrayU32(0, 0)
  301. // copy all vertex info from the decoded Object, face and index to the geometry
  302. copyVertex := func(face *Face, idx int) {
  303. var vec3 math32.Vector3
  304. var vec2 math32.Vector2
  305. pos := positions.Size() / 3
  306. // Copy vertex position and append to geometry
  307. dec.Vertices.GetVector3(3*face.Vertices[idx], &vec3)
  308. positions.AppendVector3(&vec3)
  309. // Copy vertex normal and append to geometry
  310. if face.Normals[idx] != invINDEX {
  311. dec.Normals.GetVector3(3*face.Normals[idx], &vec3)
  312. normals.AppendVector3(&vec3)
  313. }
  314. // Copy vertex uv and append to geometry
  315. if face.Uvs[idx] != invINDEX {
  316. dec.Uvs.GetVector2(2*face.Uvs[idx], &vec2)
  317. uvs.AppendVector2(&vec2)
  318. }
  319. indices.Append(uint32(pos))
  320. }
  321. var group *geometry.Group
  322. matName := ""
  323. matIndex := 0
  324. for _, face := range obj.Faces {
  325. // If face material changed, starts a new group
  326. if face.Material != matName {
  327. group = geom.AddGroup(indices.Size(), 0, matIndex)
  328. matName = face.Material
  329. matIndex++
  330. }
  331. // Copy face vertices to geometry
  332. for idx := 1; idx < len(face.Vertices)-1; idx++ {
  333. copyVertex(&face, 0)
  334. copyVertex(&face, idx)
  335. copyVertex(&face, idx+1)
  336. group.Count += 3
  337. }
  338. }
  339. geom.SetIndices(indices)
  340. geom.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
  341. geom.AddVBO(gls.NewVBO(normals).AddAttrib(gls.VertexNormal))
  342. geom.AddVBO(gls.NewVBO(uvs).AddAttrib(gls.VertexTexcoord))
  343. return geom, nil
  344. }
  345. // loadTex loads textures described in the material descriptor into the
  346. // specified material
  347. func (dec *Decoder) loadTex(mat *material.Material, desc *Material) error {
  348. // Checks if material descriptor specified texture
  349. if desc.MapKd == "" {
  350. return nil
  351. }
  352. // Get texture file path
  353. // If texture file path is not absolute assumes it is relative
  354. // to the directory of the material file
  355. var texPath string
  356. if filepath.IsAbs(desc.MapKd) {
  357. texPath = desc.MapKd
  358. } else {
  359. texPath = filepath.Join(dec.mtlDir, desc.MapKd)
  360. }
  361. // Try to load texture from image file
  362. tex, err := texture.NewTexture2DFromImage(texPath)
  363. if err != nil {
  364. return err
  365. }
  366. mat.AddTexture(tex)
  367. return nil
  368. }
  369. // parse reads the lines from the specified reader and dispatch them
  370. // to the specified line parser.
  371. func (dec *Decoder) parse(reader io.Reader, parseLine func(string) error) error {
  372. bufin := bufio.NewReader(reader)
  373. dec.line = 1
  374. for {
  375. // Reads next line and abort on errors (not EOF)
  376. line, err := bufin.ReadString('\n')
  377. if err != nil && err != io.EOF {
  378. return err
  379. }
  380. // Parses the line
  381. line = strings.Trim(line, blanks)
  382. perr := parseLine(line)
  383. if perr != nil {
  384. return perr
  385. }
  386. // If EOF ends of parsing.
  387. if err == io.EOF {
  388. break
  389. }
  390. dec.line++
  391. }
  392. return nil
  393. }
  394. // Parses obj file line, dispatching to specific parsers
  395. func (dec *Decoder) parseObjLine(line string) error {
  396. // Ignore empty lines
  397. fields := strings.Fields(line)
  398. if len(fields) == 0 {
  399. return nil
  400. }
  401. // Ignore comment lines
  402. ltype := fields[0]
  403. if strings.HasPrefix(ltype, "#") {
  404. return nil
  405. }
  406. switch ltype {
  407. // Material library
  408. case "mtllib":
  409. return dec.parseMatlib(fields[1:])
  410. // Object name
  411. case "o":
  412. return dec.parseObject(fields[1:])
  413. // Group names. We are considering "group" the same as "object"
  414. // This may not be right
  415. case "g":
  416. return dec.parseObject(fields[1:])
  417. // Vertex coordinate
  418. case "v":
  419. return dec.parseVertex(fields[1:])
  420. // Vertex normal coordinate
  421. case "vn":
  422. return dec.parseNormal(fields[1:])
  423. // Vertex texture coordinate
  424. case "vt":
  425. return dec.parseTex(fields[1:])
  426. // Face vertex
  427. case "f":
  428. return dec.parseFace(fields[1:])
  429. // Use material
  430. case "usemtl":
  431. return dec.parseUsemtl(fields[1:])
  432. // Smooth
  433. case "s":
  434. return dec.parseSmooth(fields[1:])
  435. default:
  436. dec.appendWarn(objType, "field not supported: "+ltype)
  437. }
  438. return nil
  439. }
  440. // Parses a mtllib line:
  441. // mtllib <name>
  442. func (dec *Decoder) parseMatlib(fields []string) error {
  443. if len(fields) < 1 {
  444. return errors.New("Material library (mtllib) with no fields")
  445. }
  446. dec.Matlib = fields[0]
  447. return nil
  448. }
  449. // Parses an object line:
  450. // o <name>
  451. func (dec *Decoder) parseObject(fields []string) error {
  452. if len(fields) < 1 {
  453. return errors.New("Object line (o) with no fields")
  454. }
  455. var ob Object
  456. ob.Name = fields[0]
  457. ob.Faces = make([]Face, 0)
  458. ob.materials = make([]string, 0)
  459. dec.Objects = append(dec.Objects, ob)
  460. dec.objCurrent = &dec.Objects[len(dec.Objects)-1]
  461. return nil
  462. }
  463. // Parses a vertex position line
  464. // v <x> <y> <z> [w]
  465. func (dec *Decoder) parseVertex(fields []string) error {
  466. if len(fields) < 3 {
  467. return errors.New("Less than 3 vertices in 'v' line")
  468. }
  469. for _, f := range fields[:3] {
  470. val, err := strconv.ParseFloat(f, 32)
  471. if err != nil {
  472. return err
  473. }
  474. dec.Vertices.Append(float32(val))
  475. }
  476. return nil
  477. }
  478. // Parses a vertex normal line
  479. // vn <x> <y> <z>
  480. func (dec *Decoder) parseNormal(fields []string) error {
  481. if len(fields) < 3 {
  482. return errors.New("Less than 3 normals in 'vn' line")
  483. }
  484. for _, f := range fields[:3] {
  485. val, err := strconv.ParseFloat(f, 32)
  486. if err != nil {
  487. return err
  488. }
  489. dec.Normals.Append(float32(val))
  490. }
  491. return nil
  492. }
  493. // Parses a vertex texture coordinate line:
  494. // vt <u> <v> <w>
  495. func (dec *Decoder) parseTex(fields []string) error {
  496. if len(fields) < 2 {
  497. return errors.New("Less than 2 texture coords. in 'vt' line")
  498. }
  499. for _, f := range fields[:2] {
  500. val, err := strconv.ParseFloat(f, 32)
  501. if err != nil {
  502. return err
  503. }
  504. dec.Uvs.Append(float32(val))
  505. }
  506. return nil
  507. }
  508. // parseFace parses a face decription line:
  509. // f v1[/vt1][/vn1] v2[/vt2][/vn2] v3[/vt3][/vn3] ...
  510. func (dec *Decoder) parseFace(fields []string) error {
  511. // If current object has no material, appends last material if defined
  512. if len(dec.objCurrent.materials) == 0 && dec.matCurrent != nil {
  513. dec.objCurrent.materials = append(dec.objCurrent.materials, dec.matCurrent.Name)
  514. }
  515. if len(fields) < 3 {
  516. return dec.formatError("Face line with less 3 fields")
  517. }
  518. var face Face
  519. face.Vertices = make([]int, len(fields))
  520. face.Uvs = make([]int, len(fields))
  521. face.Normals = make([]int, len(fields))
  522. if dec.matCurrent != nil {
  523. face.Material = dec.matCurrent.Name
  524. } else {
  525. // TODO (quillaja): do something better than spamming warnings for each line
  526. // dec.appendWarn(objType, "No material defined")
  527. face.Material = "internal default" // causes error on in NewGeom() if ""
  528. // dec.matCurrent = defaultMat
  529. }
  530. face.Smooth = dec.smoothCurrent
  531. for pos, f := range fields {
  532. // Separate the current field in its components: v vt vn
  533. vfields := strings.Split(f, "/")
  534. if len(vfields) < 1 {
  535. return dec.formatError("Face field with no parts")
  536. }
  537. // Get the index of this vertex position (must always exist)
  538. val, err := strconv.ParseInt(vfields[0], 10, 32)
  539. if err != nil {
  540. return err
  541. }
  542. // Positive index is an absolute vertex index
  543. if val > 0 {
  544. face.Vertices[pos] = int(val - 1)
  545. // Negative vertex index is relative to the last parsed vertex
  546. } else if val < 0 {
  547. current := (len(dec.Vertices) / 3) - 1
  548. face.Vertices[pos] = current + int(val) + 1
  549. // Vertex index could never be 0
  550. } else {
  551. return dec.formatError("Face vertex index value equal to 0")
  552. }
  553. // Get the index of this vertex UV coordinate (optional)
  554. if len(vfields) > 1 && len(vfields[1]) > 0 {
  555. val, err := strconv.ParseInt(vfields[1], 10, 32)
  556. if err != nil {
  557. return err
  558. }
  559. // Positive index is an absolute UV index
  560. if val > 0 {
  561. face.Uvs[pos] = int(val - 1)
  562. // Negative vertex index is relative to the last parsed uv
  563. } else if val < 0 {
  564. current := (len(dec.Uvs) / 2) - 1
  565. face.Uvs[pos] = current + int(val) + 1
  566. // UV index could never be 0
  567. } else {
  568. return dec.formatError("Face uv index value equal to 0")
  569. }
  570. } else {
  571. face.Uvs[pos] = invINDEX
  572. }
  573. // Get the index of this vertex normal (optional)
  574. if len(vfields) >= 3 {
  575. val, err = strconv.ParseInt(vfields[2], 10, 32)
  576. if err != nil {
  577. return err
  578. }
  579. // Positive index is an absolute normal index
  580. if val > 0 {
  581. face.Normals[pos] = int(val - 1)
  582. // Negative vertex index is relative to the last parsed normal
  583. } else if val < 0 {
  584. current := (len(dec.Normals) / 3) - 1
  585. face.Normals[pos] = current + int(val) + 1
  586. // Normal index could never be 0
  587. } else {
  588. return dec.formatError("Face normal index value equal to 0")
  589. }
  590. } else {
  591. face.Normals[pos] = invINDEX
  592. }
  593. }
  594. // Appends this face to the current object
  595. dec.objCurrent.Faces = append(dec.objCurrent.Faces, face)
  596. return nil
  597. }
  598. // parseUsemtl parses a "usemtl" decription line:
  599. // usemtl <name>
  600. func (dec *Decoder) parseUsemtl(fields []string) error {
  601. if len(fields) < 1 {
  602. return dec.formatError("Usemtl with no fields")
  603. }
  604. // Checks if this material has already been parsed
  605. name := fields[0]
  606. mat := dec.Materials[name]
  607. // Creates material descriptor
  608. if mat == nil {
  609. mat = new(Material)
  610. mat.Name = name
  611. dec.Materials[name] = mat
  612. }
  613. dec.objCurrent.materials = append(dec.objCurrent.materials, name)
  614. // Set this as the current material
  615. dec.matCurrent = mat
  616. return nil
  617. }
  618. // parseSmooth parses a "s" decription line:
  619. // s <0|1>
  620. func (dec *Decoder) parseSmooth(fields []string) error {
  621. if len(fields) < 1 {
  622. return dec.formatError("'s' with no fields")
  623. }
  624. if fields[0] == "0" || fields[0] == "off" {
  625. dec.smoothCurrent = false
  626. return nil
  627. }
  628. if fields[0] == "1" || fields[0] == "on" {
  629. dec.smoothCurrent = true
  630. return nil
  631. }
  632. return dec.formatError("'s' with invalid value")
  633. }
  634. /******************************************************************************
  635. mtl parse functions
  636. */
  637. // Parses material file line, dispatching to specific parsers
  638. func (dec *Decoder) parseMtlLine(line string) error {
  639. // Ignore empty lines
  640. fields := strings.Fields(line)
  641. if len(fields) == 0 {
  642. return nil
  643. }
  644. // Ignore comment lines
  645. ltype := fields[0]
  646. if strings.HasPrefix(ltype, "#") {
  647. return nil
  648. }
  649. switch ltype {
  650. case "newmtl":
  651. return dec.parseNewmtl(fields[1:])
  652. case "d":
  653. return dec.parseDissolve(fields[1:])
  654. case "Ka":
  655. return dec.parseKa(fields[1:])
  656. case "Kd":
  657. return dec.parseKd(fields[1:])
  658. case "Ke":
  659. return dec.parseKe(fields[1:])
  660. case "Ks":
  661. return dec.parseKs(fields[1:])
  662. case "Ni":
  663. return dec.parseNi(fields[1:])
  664. case "Ns":
  665. return dec.parseNs(fields[1:])
  666. case "illum":
  667. return dec.parseIllum(fields[1:])
  668. case "map_Kd":
  669. return dec.parseMapKd(fields[1:])
  670. default:
  671. dec.appendWarn(mtlType, "field not supported: "+ltype)
  672. }
  673. return nil
  674. }
  675. // Parses new material definition
  676. // newmtl <mat_name>
  677. func (dec *Decoder) parseNewmtl(fields []string) error {
  678. if len(fields) < 1 {
  679. return dec.formatError("newmtl with no fields")
  680. }
  681. // Checks if material has already been seen
  682. name := fields[0]
  683. mat := dec.Materials[name]
  684. // Creates material descriptor
  685. if mat == nil {
  686. mat = new(Material)
  687. mat.Name = name
  688. dec.Materials[name] = mat
  689. }
  690. dec.matCurrent = mat
  691. return nil
  692. }
  693. // Parses the dissolve factor (opacity)
  694. // d <factor>
  695. func (dec *Decoder) parseDissolve(fields []string) error {
  696. if len(fields) < 1 {
  697. return dec.formatError("'d' with no fields")
  698. }
  699. val, err := strconv.ParseFloat(fields[0], 32)
  700. if err != nil {
  701. return dec.formatError("'d' parse float error")
  702. }
  703. dec.matCurrent.Opacity = float32(val)
  704. return nil
  705. }
  706. // Parses ambient reflectivity:
  707. // Ka r g b
  708. func (dec *Decoder) parseKa(fields []string) error {
  709. if len(fields) < 3 {
  710. return dec.formatError("'Ka' with less than 3 fields")
  711. }
  712. var colors [3]float32
  713. for pos, f := range fields[:3] {
  714. val, err := strconv.ParseFloat(f, 32)
  715. if err != nil {
  716. return err
  717. }
  718. colors[pos] = float32(val)
  719. }
  720. dec.matCurrent.Ambient.Set(colors[0], colors[1], colors[2])
  721. return nil
  722. }
  723. // Parses diffuse reflectivity:
  724. // Kd r g b
  725. func (dec *Decoder) parseKd(fields []string) error {
  726. if len(fields) < 3 {
  727. return dec.formatError("'Kd' with less than 3 fields")
  728. }
  729. var colors [3]float32
  730. for pos, f := range fields[:3] {
  731. val, err := strconv.ParseFloat(f, 32)
  732. if err != nil {
  733. return err
  734. }
  735. colors[pos] = float32(val)
  736. }
  737. dec.matCurrent.Diffuse.Set(colors[0], colors[1], colors[2])
  738. return nil
  739. }
  740. // Parses emissive color:
  741. // Ke r g b
  742. func (dec *Decoder) parseKe(fields []string) error {
  743. if len(fields) < 3 {
  744. return dec.formatError("'Ke' with less than 3 fields")
  745. }
  746. var colors [3]float32
  747. for pos, f := range fields[:3] {
  748. val, err := strconv.ParseFloat(f, 32)
  749. if err != nil {
  750. return err
  751. }
  752. colors[pos] = float32(val)
  753. }
  754. dec.matCurrent.Emissive.Set(colors[0], colors[1], colors[2])
  755. return nil
  756. }
  757. // Parses specular reflectivity:
  758. // Ks r g b
  759. func (dec *Decoder) parseKs(fields []string) error {
  760. if len(fields) < 3 {
  761. return dec.formatError("'Ks' with less than 3 fields")
  762. }
  763. var colors [3]float32
  764. for pos, f := range fields[:3] {
  765. val, err := strconv.ParseFloat(f, 32)
  766. if err != nil {
  767. return err
  768. }
  769. colors[pos] = float32(val)
  770. }
  771. dec.matCurrent.Specular.Set(colors[0], colors[1], colors[2])
  772. return nil
  773. }
  774. // Parses optical density, also known as index of refraction
  775. // Ni <optical_density>
  776. func (dec *Decoder) parseNi(fields []string) error {
  777. if len(fields) < 1 {
  778. return dec.formatError("'Ni' with no fields")
  779. }
  780. val, err := strconv.ParseFloat(fields[0], 32)
  781. if err != nil {
  782. return dec.formatError("'d' parse float error")
  783. }
  784. dec.matCurrent.Refraction = float32(val)
  785. return nil
  786. }
  787. // Parses specular exponent
  788. // Ns <specular_exponent>
  789. func (dec *Decoder) parseNs(fields []string) error {
  790. if len(fields) < 1 {
  791. return dec.formatError("'Ns' with no fields")
  792. }
  793. val, err := strconv.ParseFloat(fields[0], 32)
  794. if err != nil {
  795. return dec.formatError("'d' parse float error")
  796. }
  797. dec.matCurrent.Shininess = float32(val)
  798. return nil
  799. }
  800. // Parses illumination model (0 to 10)
  801. // illum <ilum_#>
  802. func (dec *Decoder) parseIllum(fields []string) error {
  803. if len(fields) < 1 {
  804. return dec.formatError("'illum' with no fields")
  805. }
  806. val, err := strconv.ParseUint(fields[0], 10, 32)
  807. if err != nil {
  808. return dec.formatError("'d' parse int error")
  809. }
  810. dec.matCurrent.Illum = int(val)
  811. return nil
  812. }
  813. // Parses color texture linked to the diffuse reflectivity of the material
  814. // map_Kd [-options] <filename>
  815. func (dec *Decoder) parseMapKd(fields []string) error {
  816. if len(fields) < 1 {
  817. return dec.formatError("No fields")
  818. }
  819. dec.matCurrent.MapKd = fields[0]
  820. return nil
  821. }
  822. func (dec *Decoder) formatError(msg string) error {
  823. return fmt.Errorf("%s in line:%d", msg, dec.line)
  824. }
  825. func (dec *Decoder) appendWarn(ftype string, msg string) {
  826. wline := fmt.Sprintf("%s(%d): %s", ftype, dec.line, msg)
  827. dec.Warnings = append(dec.Warnings, wline)
  828. }