123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- how are we going to deal with emission??
- how do we find area light sources?
- when multiple shaders bound to mesh, are we getting that right?
- just do a full translator, including geometry
- other shader types needed? ignore geom stuff, warn about unknown nodes...
- support ramps
- do we need a new pbrt material to properly support this?
- or, maybe an 'add' material that takes an array of named materials and adds them together?
- */
- package main
- import (
- "bufio"
- "fmt"
- "io"
- "log"
- "os"
- "path"
- "strconv"
- "strings"
- )
- var lineno int
- var wTextures, wMaterials, wGeometry, wLights, wMain *bufio.Writer
- func fatal(s string) {
- fmt.Fprintf(os.Stderr, "%d: %s\n", lineno, s)
- panic(s)
- os.Exit(1)
- }
- func check(b bool, s string) {
- if !b {
- fatal(s)
- }
- }
- func parseBlock(r *bufio.Reader, ismesh bool) map[string]string {
- m := make(map[string]string)
- s, err := r.ReadString('\n')
- lineno += 1
- if err != nil {
- fatal(fmt.Sprintf("%v", err))
- }
- if strings.TrimSpace(s) != "{" {
- fatal("didn't find expected opening brace for block")
- }
- for {
- s, err := r.ReadString('\n')
- lineno += 1
- if err != nil {
- fatal(fmt.Sprintf("%v", err))
- }
- s = strings.TrimSpace(s)
- if s == "}" {
- return m
- }
- if s == "" {
- continue
- }
- nextra := 0
- if ismesh && len(s) > 4 &&
- (s[:5] == "vlist" || s[:5] == "nlist" || s[:6] == "uvlist" ||
- s[:5] == "vidxs" || s[:5] == "nidxs" || s[:6] == "uvidxs" ||
- s[:6] == "shidxs" ||
- s[:6] == "nsides") {
- nextra = 1
- }
- if ismesh && s == "matrix" {
- // TODO: are they always this way, or is this just how the c4d
- // exporter does them? More generally, are newlines allowed
- // anywhere whitespace is?
- nextra = 4
- }
- for i := 0; i < nextra; i += 1 {
- next, err := r.ReadString('\n')
- lineno += 1
- if err != nil {
- fatal(fmt.Sprintf("%v", err))
- }
- s += " " + strings.TrimSpace(next)
- }
- if ismesh {
- log.Printf("line: %s\n", s)
- }
- nv := strings.SplitAfterN(s, " ", 2)
- m[strings.TrimSpace(nv[0])] = strings.TrimSpace(nv[1])
- }
- }
- func skipBlock(r *bufio.Reader) {
- // Hack: just search for closing brace
- lastnew := false
- for {
- c, err := r.ReadByte()
- if err != nil {
- fatal(fmt.Sprintf("skipBlock error %+v", err))
- }
- if c == '}' && lastnew {
- return
- }
- lastnew = c == '\n'
- if lastnew {
- lineno += 1
- }
- }
- }
- func getFileWriterOrExit(p string) *bufio.Writer {
- f, err := os.Create(p)
- if err != nil {
- fatal(fmt.Sprintf("%s: %+v", p, err))
- }
- return bufio.NewWriter(f)
- }
- func main() {
- wTextures = getFileWriterOrExit("textures.pbrt")
- wMaterials = getFileWriterOrExit("materials.pbrt")
- // wGeometry = getFileWriterOrExit("geometry.pbrt")
- // wLights = getFileWriterOrExit("lights.pbrt")
- //wMain = getFileWriterOrExit("main.pbrt")
- r := bufio.NewReader(os.Stdin)
- // Parse the file; build up maps of materials and textures
- var mtls []map[string]string
- var images []map[string]string
- ignoredNodes := make(map[string]bool)
- for {
- s, err := r.ReadString('\n')
- lineno += 1
- if err == io.EOF {
- break
- } else if err != nil {
- fatal(fmt.Sprintf("parseass: %v", err))
- }
- scan := bufio.NewScanner(strings.NewReader(s))
- for scan.Scan() {
- cmd := strings.TrimSpace(scan.Text())
- if cmd == "" {
- continue
- } else if cmd == "standard" {
- mtls = append(mtls, parseBlock(r, false))
- } else if cmd == "image" {
- images = append(images, parseBlock(r, false))
- } else if cmd == "polymesh" {
- //emitMesh(parseBlock(r, true))
- skipBlock(r)
- } else if cmd[0] == '#' {
- // comment
- //fmt.Printf("%s\n", cmd)
- } else {
- if _, ok := ignoredNodes[cmd]; !ok {
- log.Printf("ignoring node @ line %d \"%s\"\n", lineno, cmd)
- ignoredNodes[cmd] = true
- }
- skipBlock(r)
- }
- }
- if scan.Err() != nil {
- fatal(fmt.Sprintf("parseass: scanner error %v", scan.Err()))
- }
- }
- // First emit textures
- for _, img := range images {
- name := img["name"]
- fn := strings.Trim(img["filename"], "\"")
- // FIXME HACK
- fn = path.Join("textures", fn)
- if true /* allPNG */ {
- ext := path.Ext(fn)
- if ext == ".jpg" || ext == ".JPG" || ext == ".tif" || ext == ".tiff" ||
- ext == ".TIFF" || ext == ".TIF" {
- fn = fn[:len(fn)-len(ext)] + ".png"
- }
- }
- // Emit both float and rgb variants
- fmt.Fprintf(wTextures, "Texture \"%s-float\" \"float\" \"imagemap\"\n", name)
- fmt.Fprintf(wTextures, " \"string filename\" \"%s\"\n", fn)
- fmt.Fprintf(wTextures, "Texture \"%s-color\" \"color\" \"imagemap\"\n", name)
- fmt.Fprintf(wTextures, " \"string filename\" \"%s\"\n", fn)
- }
- // Emit pbrt materials
- // https://support.solidangle.com/display/NodeRef/standard
- for _, m := range mtls {
- names := strings.Split(m["name"], "|")
- name := strings.Replace(names[1], "_", " ", -1) // TODO: do this or not?
- fmt.Fprintf(wMaterials, "MakeNamedMaterial \"%s\"\n", name)
- fmt.Fprintf(wMaterials, " \"string type\" \"substrate\"\n")
- // if m["emission"] != "0" {
- // fmt.Fprintf(wMaterials, " \"rgb Kd\" [1000 1000 1000]\n")
- // continue
- // }
- emitColorMaterialParameter("Kd", m, name)
- // TODO: "diffuse_roughness" [0,1] -> OrenNayar. Need to add to uber mtl?
- emitColorMaterialParameter("Ks", m, name)
- emitFloatMaterialParameter("specular_roughness", "uroughness", m, name)
- emitFloatMaterialParameter("specular_roughness", "vroughness", m, name)
- // specular_anisotropy
- // emitColorMaterialParameter("Kr", m, name)
- //emitColorMaterialParameter("Kt", m, name)
- // refraction_roughness
- // transmittance
- // Ior
- // Fresnel stuff?
- // emisssion?
- // Ksss ?
- // bump mapping
- // Handle opacity specially...
- if _, err := getFloat3(m, "opacity", name); err != nil {
- // Ignore constant RGB opacity, which is always ~1 for this scene
- fmt.Fprintf(wMaterials, " \"texture alpha\" \"%s-rgb\"\n", m["opacity"])
- }
- }
- if err := wTextures.Flush(); err != nil {
- fatal(fmt.Sprintf("textures.pbrt: %+v", err))
- }
- if err := wMaterials.Flush(); err != nil {
- fatal(fmt.Sprintf("materials.pbrt: %+v", err))
- }
- /*
- if err := wMain.Flush(); err != nil {
- fatal(fmt.Sprintf("main.pbrt: %+v", err))
- }
- if err := wLights.Flush(); err != nil {
- fatal(fmt.Sprintf("lights.pbrt: %+v", err))
- }
- if err := wGeometry.Flush(); err != nil {
- fatal(fmt.Sprintf("geometry.pbrt: %+v", err))
- }
- */
- }
- func emitFloatMaterialParameter(param string, emitParam string, m map[string]string, blockname string) {
- val, err := getFloat(m, param, blockname)
- if err == nil {
- fmt.Fprintf(wMaterials, " \"float %s\" %f\n", emitParam, val/10.)
- } else {
- fmt.Fprintf(wMaterials, " \"texture %s\" \"%s-float\"\n", emitParam, m[param])
- }
- }
- func emitColorMaterialParameter(param string, m map[string]string, blockname string) {
- scale, errs := getFloat(m, param, blockname)
- f3, err3 := getFloat3(m, param+"_color", blockname)
- if errs == nil {
- // Scale is a constant
- if err3 == nil {
- // It's all constants. Multiply through and return a straight up RGB.
- fmt.Fprintf(wMaterials, " \"rgb %s\" [ %f %f %f ]\n", param, f3[0]*scale, f3[1]*scale, f3[2]*scale)
- } else if scale == 1 {
- // Special case scale==1 just to keep things clean.
- fmt.Fprintf(wMaterials, " \"texture %s\" \"%s-color\"\n", param, m[param+"_color"])
- }
- } else {
- // One or more textures.
- texname := fmt.Sprintf("%s-%s", blockname, param)
- fmt.Fprintf(wTextures, "Texture \"%s\" \"color\" \"mix\" \"rgb tex1\" [ 0 0 0 ]\n", texname)
- if errs == nil {
- fmt.Fprintf(wTextures, " \"float amount\" %f\n", scale)
- } else {
- fmt.Fprintf(wTextures, " \"texture amount\" \"%s-float\"\n", m[param])
- }
- if err3 == nil {
- fmt.Fprintf(wTextures, " \"rgb tex2\" [ %f %f %f ]\n", f3[0], f3[1], f3[2])
- } else {
- fmt.Fprintf(wTextures, " \"texture tex2\" \"%s-color\"\n", m[param+"_color"])
- }
- fmt.Fprintf(wMaterials, " \"texture %s\" \"%s\"\n", param, texname)
- }
- }
- func getFloat(m map[string]string, name string, blockname string) (float64, error) {
- v, ok := m[name]
- if !ok {
- fatal(fmt.Sprintf("%s: didn't find a value for \"%s\"", blockname, name))
- }
- return strconv.ParseFloat(v, 32)
- }
- func getFloat3(m map[string]string, name string, blockname string) ([3]float64, error) {
- var f3 [3]float64
- v, ok := m[name]
- if !ok {
- return f3, fmt.Errorf("%s: didn't find a value for \"%s\"", blockname, name)
- }
- f := strings.Fields(v)
- if len(f) != 3 {
- return f3, fmt.Errorf("%s: didn't find three values after \"%s\"", blockname, name)
- }
- for i := 0; i < 3; i += 1 {
- val, err := strconv.ParseFloat(f[i], 32)
- if err != nil {
- return f3, fmt.Errorf("%s: float parse error for \"%s\": %v", blockname,
- f[i], err)
- }
- f3[i] = val
- }
- return f3, nil
- }
- func emitMesh(m map[string]string) {
- // wGeometry
- }
|