dbf_amiga

view src/scnload_obj.cc @ 0:87dfe0e10235

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 31 Aug 2015 07:38:37 +0300
parents
children
line source
1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <limits.h>
5 #include <errno.h>
6 #include <vector>
7 #include "mesh.h"
8 #include "scene.h"
9 #include "scnload.h"
11 enum {
12 CMD_VERTEX,
13 CMD_NORMAL,
14 CMD_TEXCOORD,
15 CMD_FACE,
16 CMD_OBJECT,
17 CMD_GROUP,
18 CMD_MTLLIB
19 };
21 static struct {
22 const char *s;
23 int cmd;
24 } commands[] = {
25 {"v", CMD_VERTEX},
26 {"vn", CMD_NORMAL},
27 {"vt", CMD_TEXCOORD},
28 {"f", CMD_FACE},
29 {"o", CMD_OBJECT},
30 {"g", CMD_GROUP},
31 {"mtllib", CMD_MTLLIB},
32 {0, -1}
33 };
35 struct Face {
36 int vnum;
37 int vidx[4];
38 int nidx[4];
39 int tidx[4];
40 };
42 struct ObjMesh {
43 std::vector<Vector3> varr;
44 std::vector<Vector3> narr;
45 std::vector<Vector2> tarr;
46 std::vector<Face> faces;
47 };
49 struct ParserState {
50 FILE *fp;
51 Scene *scn;
52 const char *fname;
54 unsigned int voffs, noffs, toffs;
55 ObjMesh omesh;
56 };
58 static bool parse_line(ParserState *ps, char *line);
59 static void flush_mesh(ParserState *ps);
60 static char *clean_input(char *s);
62 bool load_obj(Scene *scn, const char *fname)
63 {
64 FILE *fp;
65 char buf[256];
66 bool result = false;
67 ParserState ps;
69 if(!(fp = fopen(fname, "r"))) {
70 fprintf(stderr, "failed to open obj file: %s: %s\n", fname, strerror(errno));
71 return false;
72 }
73 ps.fp = fp;
74 ps.scn = scn;
75 ps.fname = fname;
76 ps.voffs = ps.noffs = ps.toffs = 0;
78 int nline = 0;
79 while(fgets(buf, sizeof buf, fp)) {
80 ++nline;
81 char *line = clean_input(buf);
82 if(!*line) continue;
84 if(!parse_line(&ps, line)) {
85 fprintf(stderr, "[obj] %s:%d: failed to parse\n", fname, nline);
86 goto done;
87 }
88 }
89 flush_mesh(&ps);
90 result = true;
92 done:
93 fclose(fp);
94 return result;
95 }
97 static int parse_cmd(char *line, char **endp)
98 {
99 char *end = line;
100 while(*end && !isspace(*end)) end++;
101 *end = 0;
102 *endp = end;
104 for(int i=0; commands[i].s; i++) {
105 if(strcmp(commands[i].s, line) == 0) {
106 return commands[i].cmd;
107 }
108 }
109 return -1;
110 }
112 static bool parse_vec3(Vector3 *vp, char *line)
113 {
114 return sscanf(line, "%f %f %f", &vp->x, &vp->y, &vp->z) == 3;
115 }
117 static bool parse_vec2(Vector2 *vp, char *line)
118 {
119 return sscanf(line, "%f %f", &vp->x, &vp->y) == 2;
120 }
122 #define INVAL_IDX INT_MIN
124 static bool parse_face(Face *fp, char *line)
125 {
126 fp->vnum = 0;
128 int res;
129 char *tok, *ptr = line;
130 for(int i=0; i<4; i++) {
131 while(*ptr && isspace(*ptr)) ptr++;
132 if(!*ptr) break;
133 tok = ptr;
135 fp->vnum++;
137 while(*ptr && !isspace(*ptr)) ptr++;
138 if(*ptr) *ptr++ = 0;
140 // parse the index descriptor vidx[/[tidx]/[nidx]]
141 if(sscanf(tok, "%d//%d", &fp->vidx[i], &fp->nidx[i]) == 2) {
142 fp->tidx[i] = INVAL_IDX;
143 continue;
144 }
145 res = sscanf(tok, "%d/%d/%d", &fp->vidx[i], &fp->tidx[i], &fp->nidx[i]);
146 if(res > 2) {
147 if(res != 3) {
148 fp->nidx[i] = INVAL_IDX;
149 }
150 continue;
151 }
152 char *endp;
153 fp->vidx[i] = strtol(tok, &endp, 10);
154 if(endp == tok) {
155 return false;
156 }
157 fp->nidx[i] = fp->tidx[i] = INVAL_IDX;
158 }
159 return fp->vnum >= 3 ? true : false;
160 }
162 static bool parse_line(ParserState *ps, char *line)
163 {
164 char *endp;
165 int cmd = parse_cmd(line, &endp);
166 if(cmd == -1) return true; // ignore unknown commands
167 line = endp + 1;
168 while(*line && isspace(*line)) line++;
170 Vector3 v;
171 Vector2 v2;
172 Face f;
174 switch(cmd) {
175 case CMD_VERTEX:
176 if(!parse_vec3(&v, line)) return false;
177 ps->omesh.varr.push_back(v);
178 break;
180 case CMD_NORMAL:
181 if(!parse_vec3(&v, line)) return false;
182 ps->omesh.narr.push_back(v);
183 break;
185 case CMD_TEXCOORD:
186 if(!parse_vec2(&v2, line)) return false;
187 ps->omesh.tarr.push_back(v2);
188 break;
190 case CMD_FACE:
191 if(!parse_face(&f, line)) return false;
192 ps->omesh.faces.push_back(f);
193 break;
195 case CMD_OBJECT:
196 case CMD_GROUP:
197 flush_mesh(ps);
198 break;
200 default:
201 break;
202 }
203 return true;
204 }
206 static void flush_mesh(ParserState *ps)
207 {
208 if(!ps->omesh.varr.empty() && !ps->omesh.faces.empty()) {
209 int num_verts = (int)ps->omesh.varr.size();
210 int num_faces = (int)ps->omesh.faces.size();
212 Mesh *m = new Mesh;
213 m->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)&ps->omesh.varr[0]);
215 if(!ps->omesh.narr.empty()) {
216 m->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)&ps->omesh.narr[0]);
217 }
218 if(!ps->omesh.tarr.empty()) {
219 m->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, (float*)&ps->omesh.tarr[0]);
220 }
222 unsigned int *idxarr = m->set_index_data(num_faces * 3);
223 for(int i=0; i<num_faces; i++) {
224 for(int j=0; j<3; j++) {
225 int idx = ps->omesh.faces[i].vidx[j] - ps->voffs - 1; // TODO normalize indices
226 *idxarr++ = idx;
227 }
228 }
230 Object *o = new Object;
231 o->set_mesh(m);
233 // TODO material & textures
235 ps->scn->add_object(o);
237 printf("added object with %d vertices and %d faces\n", num_verts, num_faces);
238 }
240 ps->voffs += ps->omesh.varr.size();
241 ps->noffs += ps->omesh.narr.size();
242 ps->toffs += ps->omesh.tarr.size();
244 ps->omesh.varr.clear();
245 ps->omesh.narr.clear();
246 ps->omesh.tarr.clear();
247 ps->omesh.faces.clear();
248 }
250 static char *clean_input(char *s)
251 {
252 while(*s && isspace(*s)) s++;
253 if(!*s) return s;
255 char *end = strchr(s, '#');
256 if(end) *end = 0;
258 end = s + strlen(s) - 1;
259 while(end > s && isspace(*end)) end--;
260 end[1] = 0;
262 return s;
263 }