parseass.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. how are we going to deal with emission??
  3. how do we find area light sources?
  4. when multiple shaders bound to mesh, are we getting that right?
  5. just do a full translator, including geometry
  6. other shader types needed? ignore geom stuff, warn about unknown nodes...
  7. support ramps
  8. do we need a new pbrt material to properly support this?
  9. or, maybe an 'add' material that takes an array of named materials and adds them together?
  10. */
  11. package main
  12. import (
  13. "bufio"
  14. "fmt"
  15. "io"
  16. "log"
  17. "os"
  18. "path"
  19. "strconv"
  20. "strings"
  21. )
  22. var lineno int
  23. var wTextures, wMaterials, wGeometry, wLights, wMain *bufio.Writer
  24. func fatal(s string) {
  25. fmt.Fprintf(os.Stderr, "%d: %s\n", lineno, s)
  26. panic(s)
  27. os.Exit(1)
  28. }
  29. func check(b bool, s string) {
  30. if !b {
  31. fatal(s)
  32. }
  33. }
  34. func parseBlock(r *bufio.Reader, ismesh bool) map[string]string {
  35. m := make(map[string]string)
  36. s, err := r.ReadString('\n')
  37. lineno += 1
  38. if err != nil {
  39. fatal(fmt.Sprintf("%v", err))
  40. }
  41. if strings.TrimSpace(s) != "{" {
  42. fatal("didn't find expected opening brace for block")
  43. }
  44. for {
  45. s, err := r.ReadString('\n')
  46. lineno += 1
  47. if err != nil {
  48. fatal(fmt.Sprintf("%v", err))
  49. }
  50. s = strings.TrimSpace(s)
  51. if s == "}" {
  52. return m
  53. }
  54. if s == "" {
  55. continue
  56. }
  57. nextra := 0
  58. if ismesh && len(s) > 4 &&
  59. (s[:5] == "vlist" || s[:5] == "nlist" || s[:6] == "uvlist" ||
  60. s[:5] == "vidxs" || s[:5] == "nidxs" || s[:6] == "uvidxs" ||
  61. s[:6] == "shidxs" ||
  62. s[:6] == "nsides") {
  63. nextra = 1
  64. }
  65. if ismesh && s == "matrix" {
  66. // TODO: are they always this way, or is this just how the c4d
  67. // exporter does them? More generally, are newlines allowed
  68. // anywhere whitespace is?
  69. nextra = 4
  70. }
  71. for i := 0; i < nextra; i += 1 {
  72. next, err := r.ReadString('\n')
  73. lineno += 1
  74. if err != nil {
  75. fatal(fmt.Sprintf("%v", err))
  76. }
  77. s += " " + strings.TrimSpace(next)
  78. }
  79. if ismesh {
  80. log.Printf("line: %s\n", s)
  81. }
  82. nv := strings.SplitAfterN(s, " ", 2)
  83. m[strings.TrimSpace(nv[0])] = strings.TrimSpace(nv[1])
  84. }
  85. }
  86. func skipBlock(r *bufio.Reader) {
  87. // Hack: just search for closing brace
  88. lastnew := false
  89. for {
  90. c, err := r.ReadByte()
  91. if err != nil {
  92. fatal(fmt.Sprintf("skipBlock error %+v", err))
  93. }
  94. if c == '}' && lastnew {
  95. return
  96. }
  97. lastnew = c == '\n'
  98. if lastnew {
  99. lineno += 1
  100. }
  101. }
  102. }
  103. func getFileWriterOrExit(p string) *bufio.Writer {
  104. f, err := os.Create(p)
  105. if err != nil {
  106. fatal(fmt.Sprintf("%s: %+v", p, err))
  107. }
  108. return bufio.NewWriter(f)
  109. }
  110. func main() {
  111. wTextures = getFileWriterOrExit("textures.pbrt")
  112. wMaterials = getFileWriterOrExit("materials.pbrt")
  113. // wGeometry = getFileWriterOrExit("geometry.pbrt")
  114. // wLights = getFileWriterOrExit("lights.pbrt")
  115. //wMain = getFileWriterOrExit("main.pbrt")
  116. r := bufio.NewReader(os.Stdin)
  117. // Parse the file; build up maps of materials and textures
  118. var mtls []map[string]string
  119. var images []map[string]string
  120. ignoredNodes := make(map[string]bool)
  121. for {
  122. s, err := r.ReadString('\n')
  123. lineno += 1
  124. if err == io.EOF {
  125. break
  126. } else if err != nil {
  127. fatal(fmt.Sprintf("parseass: %v", err))
  128. }
  129. scan := bufio.NewScanner(strings.NewReader(s))
  130. for scan.Scan() {
  131. cmd := strings.TrimSpace(scan.Text())
  132. if cmd == "" {
  133. continue
  134. } else if cmd == "standard" {
  135. mtls = append(mtls, parseBlock(r, false))
  136. } else if cmd == "image" {
  137. images = append(images, parseBlock(r, false))
  138. } else if cmd == "polymesh" {
  139. //emitMesh(parseBlock(r, true))
  140. skipBlock(r)
  141. } else if cmd[0] == '#' {
  142. // comment
  143. //fmt.Printf("%s\n", cmd)
  144. } else {
  145. if _, ok := ignoredNodes[cmd]; !ok {
  146. log.Printf("ignoring node @ line %d \"%s\"\n", lineno, cmd)
  147. ignoredNodes[cmd] = true
  148. }
  149. skipBlock(r)
  150. }
  151. }
  152. if scan.Err() != nil {
  153. fatal(fmt.Sprintf("parseass: scanner error %v", scan.Err()))
  154. }
  155. }
  156. // First emit textures
  157. for _, img := range images {
  158. name := img["name"]
  159. fn := strings.Trim(img["filename"], "\"")
  160. // FIXME HACK
  161. fn = path.Join("textures", fn)
  162. if true /* allPNG */ {
  163. ext := path.Ext(fn)
  164. if ext == ".jpg" || ext == ".JPG" || ext == ".tif" || ext == ".tiff" ||
  165. ext == ".TIFF" || ext == ".TIF" {
  166. fn = fn[:len(fn)-len(ext)] + ".png"
  167. }
  168. }
  169. // Emit both float and rgb variants
  170. fmt.Fprintf(wTextures, "Texture \"%s-float\" \"float\" \"imagemap\"\n", name)
  171. fmt.Fprintf(wTextures, " \"string filename\" \"%s\"\n", fn)
  172. fmt.Fprintf(wTextures, "Texture \"%s-color\" \"color\" \"imagemap\"\n", name)
  173. fmt.Fprintf(wTextures, " \"string filename\" \"%s\"\n", fn)
  174. }
  175. // Emit pbrt materials
  176. // https://support.solidangle.com/display/NodeRef/standard
  177. for _, m := range mtls {
  178. names := strings.Split(m["name"], "|")
  179. name := strings.Replace(names[1], "_", " ", -1) // TODO: do this or not?
  180. fmt.Fprintf(wMaterials, "MakeNamedMaterial \"%s\"\n", name)
  181. fmt.Fprintf(wMaterials, " \"string type\" \"substrate\"\n")
  182. // if m["emission"] != "0" {
  183. // fmt.Fprintf(wMaterials, " \"rgb Kd\" [1000 1000 1000]\n")
  184. // continue
  185. // }
  186. emitColorMaterialParameter("Kd", m, name)
  187. // TODO: "diffuse_roughness" [0,1] -> OrenNayar. Need to add to uber mtl?
  188. emitColorMaterialParameter("Ks", m, name)
  189. emitFloatMaterialParameter("specular_roughness", "uroughness", m, name)
  190. emitFloatMaterialParameter("specular_roughness", "vroughness", m, name)
  191. // specular_anisotropy
  192. // emitColorMaterialParameter("Kr", m, name)
  193. //emitColorMaterialParameter("Kt", m, name)
  194. // refraction_roughness
  195. // transmittance
  196. // Ior
  197. // Fresnel stuff?
  198. // emisssion?
  199. // Ksss ?
  200. // bump mapping
  201. // Handle opacity specially...
  202. if _, err := getFloat3(m, "opacity", name); err != nil {
  203. // Ignore constant RGB opacity, which is always ~1 for this scene
  204. fmt.Fprintf(wMaterials, " \"texture alpha\" \"%s-rgb\"\n", m["opacity"])
  205. }
  206. }
  207. if err := wTextures.Flush(); err != nil {
  208. fatal(fmt.Sprintf("textures.pbrt: %+v", err))
  209. }
  210. if err := wMaterials.Flush(); err != nil {
  211. fatal(fmt.Sprintf("materials.pbrt: %+v", err))
  212. }
  213. /*
  214. if err := wMain.Flush(); err != nil {
  215. fatal(fmt.Sprintf("main.pbrt: %+v", err))
  216. }
  217. if err := wLights.Flush(); err != nil {
  218. fatal(fmt.Sprintf("lights.pbrt: %+v", err))
  219. }
  220. if err := wGeometry.Flush(); err != nil {
  221. fatal(fmt.Sprintf("geometry.pbrt: %+v", err))
  222. }
  223. */
  224. }
  225. func emitFloatMaterialParameter(param string, emitParam string, m map[string]string, blockname string) {
  226. val, err := getFloat(m, param, blockname)
  227. if err == nil {
  228. fmt.Fprintf(wMaterials, " \"float %s\" %f\n", emitParam, val/10.)
  229. } else {
  230. fmt.Fprintf(wMaterials, " \"texture %s\" \"%s-float\"\n", emitParam, m[param])
  231. }
  232. }
  233. func emitColorMaterialParameter(param string, m map[string]string, blockname string) {
  234. scale, errs := getFloat(m, param, blockname)
  235. f3, err3 := getFloat3(m, param+"_color", blockname)
  236. if errs == nil {
  237. // Scale is a constant
  238. if err3 == nil {
  239. // It's all constants. Multiply through and return a straight up RGB.
  240. fmt.Fprintf(wMaterials, " \"rgb %s\" [ %f %f %f ]\n", param, f3[0]*scale, f3[1]*scale, f3[2]*scale)
  241. } else if scale == 1 {
  242. // Special case scale==1 just to keep things clean.
  243. fmt.Fprintf(wMaterials, " \"texture %s\" \"%s-color\"\n", param, m[param+"_color"])
  244. }
  245. } else {
  246. // One or more textures.
  247. texname := fmt.Sprintf("%s-%s", blockname, param)
  248. fmt.Fprintf(wTextures, "Texture \"%s\" \"color\" \"mix\" \"rgb tex1\" [ 0 0 0 ]\n", texname)
  249. if errs == nil {
  250. fmt.Fprintf(wTextures, " \"float amount\" %f\n", scale)
  251. } else {
  252. fmt.Fprintf(wTextures, " \"texture amount\" \"%s-float\"\n", m[param])
  253. }
  254. if err3 == nil {
  255. fmt.Fprintf(wTextures, " \"rgb tex2\" [ %f %f %f ]\n", f3[0], f3[1], f3[2])
  256. } else {
  257. fmt.Fprintf(wTextures, " \"texture tex2\" \"%s-color\"\n", m[param+"_color"])
  258. }
  259. fmt.Fprintf(wMaterials, " \"texture %s\" \"%s\"\n", param, texname)
  260. }
  261. }
  262. func getFloat(m map[string]string, name string, blockname string) (float64, error) {
  263. v, ok := m[name]
  264. if !ok {
  265. fatal(fmt.Sprintf("%s: didn't find a value for \"%s\"", blockname, name))
  266. }
  267. return strconv.ParseFloat(v, 32)
  268. }
  269. func getFloat3(m map[string]string, name string, blockname string) ([3]float64, error) {
  270. var f3 [3]float64
  271. v, ok := m[name]
  272. if !ok {
  273. return f3, fmt.Errorf("%s: didn't find a value for \"%s\"", blockname, name)
  274. }
  275. f := strings.Fields(v)
  276. if len(f) != 3 {
  277. return f3, fmt.Errorf("%s: didn't find three values after \"%s\"", blockname, name)
  278. }
  279. for i := 0; i < 3; i += 1 {
  280. val, err := strconv.ParseFloat(f[i], 32)
  281. if err != nil {
  282. return f3, fmt.Errorf("%s: float parse error for \"%s\": %v", blockname,
  283. f[i], err)
  284. }
  285. f3[i] = val
  286. }
  287. return f3, nil
  288. }
  289. func emitMesh(m map[string]string) {
  290. // wGeometry
  291. }