mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-18 03:04:48 +00:00
244 lines
7.5 KiB
C++
244 lines
7.5 KiB
C++
|
/* 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;
|
||
|
}
|
||
|
|
||
|
void model::load_obj(model_type& objects) {
|
||
|
fhstream fs(path+fname, "rt");
|
||
|
|
||
|
if(!fs.f)
|
||
|
throw hr_exception("failed to open model file: " + path + fname);
|
||
|
|
||
|
vector<hyperpoint> vertices;
|
||
|
vector<hyperpoint> normals;
|
||
|
vector<hyperpoint> 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<object>());
|
||
|
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<vertexinfo, 3> vis;
|
||
|
vector<hyperpoint> hys;
|
||
|
vector<hyperpoint> 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<parts; a++)
|
||
|
for(int b=0; b<parts-a; b++) {
|
||
|
tri(a, b);
|
||
|
tri(a+1, b);
|
||
|
tri(a, b+1);
|
||
|
if(a+b < parts-1) {
|
||
|
tri(a, b+1);
|
||
|
tri(a+1, b);
|
||
|
tri(a+1, b+1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(s == "l") {
|
||
|
int a, b;
|
||
|
scan(fs, a, b);
|
||
|
/* ignore */
|
||
|
}
|
||
|
else if(s == "") { }
|
||
|
else
|
||
|
throw hr_exception("unknown command: " + s);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
throw("unknown command: " + s);
|
||
|
}
|
||
|
|
||
|
println(hlog, "reading finished");
|
||
|
|
||
|
cgi.extra_vertices();
|
||
|
}
|
||
|
|
||
|
void model::render(const shiftmatrix& V) {
|
||
|
|
||
|
auto& objs = models[cgi_string()];
|
||
|
|
||
|
if(objs.empty()) load_obj(objs);
|
||
|
|
||
|
for(auto& obj: objs) {
|
||
|
queuepoly(V, obj->sh, obj->color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|