/* a library for loading 3D models */ #include "rogueviz.h" namespace rogueviz { namespace objmodels { bool scan(fhstream& hs, char& c) { return fscanf(hs.f, "%c", &c) == 1; } char peek(fhstream& fs) { char g = fgetc(fs.f); ungetc(g, fs.f); return g; } bool model::available() { if(av_checked) return is_available; av_checked = true; is_available = false; return false; } void model::load_obj(model_data& md) { auto& objects = md.objs; fhstream fs(path+fname, "rt"); if(!fs.f) throw hr_exception("failed to open model file: " + path + fname); preparer(); vector vertices; vector normals; vector tvertices; while(!feof(fs.f)) { string s; scan(fs, s); if(s == "#") { scanline(fs); } else if(s == "mtllib") { string mtllib; scan(fs, mtllib); fhstream fsm(path+mtllib, "rt"); if(!fsm.f) throw hr_exception("failed to open mtllib: " + mtllib); color_t nextcol = 0xFFFFFFFF; string mtlname, texname = ""; auto emit_material = [&] { if(texname != "") { texture::texture_data tdata; materials[mtlname] = tdata; auto& mat = materials[mtlname]; mat.twidth = mat.theight = 0; mat.stretched = true; println(hlog, "texname: ", texname); mat.readtexture(path+texname); mat.loadTextureGL(); println(hlog, "texture ID: ", mat.textureid); } colors[mtlname] = nextcol; println(hlog, "color of ", mtlname, " is ", nextcol); }; while(!feof(fsm.f)) { string s; scan(fsm, s); if(s == "#") { scanline(fsm); } if(s == "Kd") { ld a, b, c; scan(fsm, a, b, c); part(nextcol, 1) = a * 319.99; part(nextcol, 2) = b * 319.99; part(nextcol, 3) = c * 319.99; } if(s == "newmtl") { emit_material(); nextcol = 0xFFFFFFFF; texname = ""; mtlname = scanline(fsm); } if(s == "map_Kd") { scan(fsm, texname); } } emit_material(); } else if(s == "o" || s == "g") { next_object: object *co = nullptr; bool textured = false; string oname = scanline(fs); println(hlog, "reading object: ", oname); while(true) { if(feof(fs.f)) { if(co) cgi.finishshape(); if(co) println(hlog, "vertices = ", co->sh.e-co->sh.s, " tvertices = ", isize(co->tv.tvertices)); break; } scan(fs, s); if(s == "#") { scanline(fs); } else if(s == "o" || s == "g") { if(co) cgi.finishshape(); if(co) println(hlog, "vertices = ", co->sh.e-co->sh.s, " tvertices = ", isize(co->tv.tvertices)); goto next_object; } else if(s == "v") { hyperpoint h = C0; scan(fs, h[0], h[1], h[2]); // assume all h[0] /= 100; h[1] /= 100; h[2] /= 100; vertices.push_back(h); } else if(s == "vt") { ld u, v; scan(fs, u, v); tvertices.push_back(point3(u, 1-v, 0)); } else if(s == "vn") { hyperpoint hn = C0; scan(fs, hn[0], hn[1], hn[2]); normals.push_back(hn); } else if(s == "s") { scan(fs, s); } else if(s == "usemtl") { if(co) cgi.finishshape(); if(co) println(hlog, "vertices = ", co->sh.e-co->sh.s, " tvertices = ", isize(co->tv.tvertices)); string mtlname = scanline(fs); co = nullptr; if(mtlname.find("Layer_Layer0") != string::npos) continue; objects.push_back(make_shared()); co = &*objects.back(); cgi.bshape(co->sh, PPR::WALL); cgi.last->flags |= POLY_TRIANGLES; cgi.last->texture_offset = 0; if(materials.count(mtlname)) { textured = true; cgi.last->tinf = &co->tv; co->tv.texture_id = materials[mtlname].textureid; println(hlog, "using texture_id : ", co->tv.texture_id); co->color = 0xFFFFFFFF; } else { textured = false; cgi.last->tinf = &co->tv; co->tv.texture_id = floor_textures->renderedTexture; if(colors.count(mtlname)) co->color = colors[mtlname]; else co->color = 0xFFFFFFFF; } println(hlog, "set textured to ", textured); } else if(s == "f") { struct vertexinfo { int f, t, n; }; array vis; vector hys; vector tot; char bar; for(int i=0; i<3; i++) { vis[i].f = vis[i].t = vis[i].n = 1; scan(fs, vis[i].f); if(peek(fs) == '/') { scan(fs, bar); if(peek(fs) != '/') scan(fs, vis[i].t); } if(peek(fs) == '/') { scan(fs, bar); scan(fs, vis[i].n); } vis[i].f--; vis[i].t--; vis[i].n--; if(vis[i].f < 0 || vis[i].f >= isize(vertices)) throw hr_exception("illegal ID"); hys.push_back(vertices[vis[i].f]); tot.push_back(textured ? tvertices[vis[i].t] : point3(0,0,0)); } if(!co) continue; hyperpoint norm = (hys[1] - hys[0]) ^ (hys[2] - hys[0]); norm /= hypot_d(3, norm); ld y = .5 + (.2 * norm[0] + .16 * norm[1] + .14 * norm[2]); glvertex shade = glhr::makevertex(0, y, 0); glvertex shadecol = glhr::makevertex(y, y, y); auto n0 = tf(hys[0]); auto n1 = tf(hys[1]); auto n2 = tf(hys[2]); auto mi = min(n0.first, min(n1.first, n2.first)); auto ma = max(n0.first, max(n1.first, n2.first)); if(ma - mi > 1) continue; int parts = sd(hys); auto tri = [&] (int a, int b) { cgi.hpcpush(tf(hys[0] + (hys[1] - hys[0]) * a / parts + (hys[2] - hys[0]) * b / parts).second); // cgi.hpcpush(tf(tot[0] + (tot[1] - tot[0]) * a / parts + (tot[2] - tot[0]) * b / parts).second); if(textured) { co->tv.tvertices.push_back(glhr::pointtogl(tot[0] + (tot[1] - tot[0]) * a / parts + (tot[2] - tot[0]) * b / parts)); co->tv.colors.push_back(shadecol); } else { co->tv.tvertices.push_back(shade); } }; for(int a=0; a&) cgi.ext[fname]; if(!md) { md = std::make_unique(); load_obj(*md); } return *md; } void model_data::render(const shiftmatrix& V) { for(auto& obj: objs) { queuepoly(V, obj->sh, obj->color); } } } }