Capture the Flag, 2008 Jun 17
[SauerbratenRemote.git] / src / engine / obj.h
blob686b770c57b22b0c44849b563c681c2069b58709
1 struct obj;
3 obj *loadingobj = 0;
5 string objdir;
7 struct obj : vertmodel
9 obj(const char *name) : vertmodel(name) {}
11 int type() const { return MDL_OBJ; }
13 struct objmeshgroup : vertmeshgroup
15 void parsevert(char *s, vector<vec> &out)
17 vec &v = out.add(vec(0, 0, 0));
18 while(isalpha(*s)) s++;
19 loopi(3)
21 v[i] = strtod(s, &s);
22 while(isspace(*s)) s++;
23 if(!*s) break;
27 bool load(char *filename)
29 int len = strlen(filename);
30 if(len < 4 || strcasecmp(&filename[len-4], ".obj")) return false;
32 FILE *file = openfile(filename, "rb");
33 if(!file) return false;
35 name = newstring(filename);
37 numframes = 1;
39 vector<vec> attrib[3];
40 char buf[512];
42 hashtable<ivec, int> verthash;
43 vector<vert> verts;
44 vector<tcvert> tcverts;
45 vector<tri> tris;
47 #define STARTMESH do { \
48 vertmesh &m = *new vertmesh; \
49 m.group = this; \
50 m.name = meshname[0] ? newstring(meshname) : NULL; \
51 meshes.add(&m); \
52 curmesh = &m; \
53 verthash.clear(); \
54 verts.setsizenodelete(0); \
55 tcverts.setsizenodelete(0); \
56 tris.setsizenodelete(0); \
57 } while(0)
59 #define FLUSHMESH do { \
60 curmesh->numverts = verts.length(); \
61 if(verts.length()) \
62 { \
63 curmesh->verts = new vert[verts.length()]; \
64 memcpy(curmesh->verts, verts.getbuf(), verts.length()*sizeof(vert)); \
65 curmesh->tcverts = new tcvert[verts.length()]; \
66 memcpy(curmesh->tcverts, tcverts.getbuf(), tcverts.length()*sizeof(tcvert)); \
67 } \
68 curmesh->numtris = tris.length(); \
69 if(tris.length()) \
70 { \
71 curmesh->tris = new tri[tris.length()]; \
72 memcpy(curmesh->tris, tris.getbuf(), tris.length()*sizeof(tri)); \
73 } \
74 } while(0)
76 string meshname = "";
77 vertmesh *curmesh = NULL;
78 for(;;)
80 fgets(buf, sizeof(buf), file);
81 if(feof(file)) break;
82 char *c = buf;
83 while(isspace(*c)) c++;
84 switch(*c)
86 case '#': continue;
87 case 'v':
88 if(isspace(c[1])) parsevert(c, attrib[0]);
89 else if(c[1]=='t') parsevert(c, attrib[1]);
90 else if(c[1]=='n') parsevert(c, attrib[2]);
91 break;
92 case 'g':
94 while(isalpha(*c)) c++;
95 while(isspace(*c)) c++;
96 char *name = c;
97 size_t namelen = strlen(name);
98 while(namelen > 0 && isspace(name[namelen-1])) namelen--;
99 s_strncpy(meshname, name, min(namelen+1, sizeof(meshname)));
101 if(curmesh) FLUSHMESH;
102 curmesh = NULL;
103 break;
105 case 'f':
107 if(!curmesh) STARTMESH;
108 int v0 = -1, v1 = -1;
109 while(isalpha(*c)) c++;
110 for(;;)
112 while(isspace(*c)) c++;
113 if(!*c) break;
114 ivec vkey(-1, -1, -1);
115 loopi(3)
117 vkey[i] = strtol(c, &c, 10);
118 if(vkey[i] < 0) vkey[i] = attrib[i].length() - vkey[i];
119 else vkey[i]--;
120 if(!attrib[i].inrange(vkey[i])) vkey[i] = -1;
121 if(*c!='/') break;
122 c++;
124 int *index = verthash.access(vkey);
125 if(!index)
127 index = &verthash[vkey];
128 *index = verts.length();
129 vert &v = verts.add();
130 v.pos = vkey.x < 0 ? vec(0, 0, 0) : attrib[0][vkey.x];
131 v.norm = vkey.z < 0 ? vec(0, 0, 0) : attrib[2][vkey.z];
132 tcvert &tcv = tcverts.add();
133 if(vkey.y < 0) tcv.u = tcv.v = 0;
134 else { tcv.u = attrib[1][vkey.y].x; tcv.v = attrib[1][vkey.y].y; }
136 if(v0 < 0) v0 = *index;
137 else if(v1 < 0) v1 = *index;
138 else
140 tri &t = tris.add();
141 t.vert[0] = ushort(v0);
142 t.vert[1] = ushort(v1);
143 t.vert[2] = ushort(*index);
144 v1 = *index;
147 break;
152 if(curmesh) FLUSHMESH;
154 fclose(file);
156 return true;
160 meshgroup *loadmeshes(char *name, va_list args)
162 objmeshgroup *group = new objmeshgroup;
163 if(!group->load(name)) { delete group; return NULL; }
164 return group;
167 bool loaddefaultparts()
169 part &mdl = *new part;
170 parts.add(&mdl);
171 mdl.model = this;
172 mdl.index = 0;
173 const char *pname = parentdir(loadname);
174 s_sprintfd(name1)("packages/models/%s/tris.obj", loadname);
175 mdl.meshes = sharemeshes(path(name1));
176 if(!mdl.meshes)
178 s_sprintfd(name2)("packages/models/%s/tris.obj", pname); // try obj in parent folder (vert sharing)
179 mdl.meshes = sharemeshes(path(name2));
180 if(!mdl.meshes) return false;
182 Texture *tex, *masks;
183 loadskin(loadname, pname, tex, masks);
184 mdl.initskins(tex, masks);
185 if(tex==notexture) conoutf("could not load model skin for %s", name1);
186 return true;
189 bool load()
191 if(loaded) return true;
192 s_sprintf(objdir)("packages/models/%s", loadname);
193 s_sprintfd(cfgname)("packages/models/%s/obj.cfg", loadname);
195 loadingobj = this;
196 persistidents = false;
197 if(execfile(cfgname) && parts.length()) // configured obj, will call the obj* commands below
199 persistidents = true;
200 loadingobj = NULL;
201 loopv(parts) if(!parts[i]->meshes) return false;
203 else // obj without configuration, try default tris and skin
205 persistidents = true;
206 loadingobj = NULL;
207 if(!loaddefaultparts()) return false;
209 loopv(parts) parts[i]->meshes = parts[i]->meshes->scaleverts(scale/4.0f, i ? vec(0, 0, 0) : vec(translate.x, -translate.y, translate.z));
210 return loaded = true;
214 void objload(char *model)
216 if(!loadingobj) { conoutf("not loading an obj"); return; }
217 s_sprintfd(filename)("%s/%s", objdir, model);
218 obj::part &mdl = *new obj::part;
219 loadingobj->parts.add(&mdl);
220 mdl.model = loadingobj;
221 mdl.index = loadingobj->parts.length()-1;
222 if(mdl.index) mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
223 mdl.meshes = loadingobj->sharemeshes(path(filename));
224 if(!mdl.meshes) conoutf("could not load %s", filename); // ignore failure
225 else mdl.initskins();
228 void objpitch(float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
230 if(!loadingobj || loadingobj->parts.empty()) { conoutf("not loading an obj"); return; }
231 obj::part &mdl = *loadingobj->parts.last();
233 mdl.pitchscale = *pitchscale;
234 mdl.pitchoffset = *pitchoffset;
235 if(*pitchmin || *pitchmax)
237 mdl.pitchmin = *pitchmin;
238 mdl.pitchmax = *pitchmax;
240 else
242 mdl.pitchmin = -360*mdl.pitchscale;
243 mdl.pitchmax = 360*mdl.pitchscale;
247 #define loopobjmeshes(meshname, m, body) \
248 if(!loadingobj || loadingobj->parts.empty()) { conoutf("not loading an obj"); return; } \
249 obj::part &mdl = *loadingobj->parts.last(); \
250 if(!mdl.meshes) return; \
251 loopv(mdl.meshes->meshes) \
253 obj::vertmesh &m = *(obj::vertmesh *)mdl.meshes->meshes[i]; \
254 if(!strcmp(meshname, "*") || (m.name && !strcmp(m.name, meshname))) \
256 body; \
260 #define loopobjskins(meshname, s, body) loopobjmeshes(meshname, m, { obj::skin &s = mdl.skins[i]; body; })
262 void objskin(char *meshname, char *tex, char *masks, float *envmapmax, float *envmapmin)
264 loopobjskins(meshname, s,
265 s.tex = textureload(makerelpath(objdir, tex), 0, true, false);
266 if(*masks)
268 s.masks = textureload(makerelpath(objdir, masks, "<ffmask:25>"), 0, true, false);
269 s.envmapmax = *envmapmax;
270 s.envmapmin = *envmapmin;
275 void objspec(char *meshname, int *percent)
277 float spec = 1.0f;
278 if(*percent>0) spec = *percent/100.0f;
279 else if(*percent<0) spec = 0.0f;
280 loopobjskins(meshname, s, s.spec = spec);
283 void objambient(char *meshname, int *percent)
285 float ambient = 0.3f;
286 if(*percent>0) ambient = *percent/100.0f;
287 else if(*percent<0) ambient = 0.0f;
288 loopobjskins(meshname, s, s.ambient = ambient);
291 void objglow(char *meshname, int *percent)
293 float glow = 3.0f;
294 if(*percent>0) glow = *percent/100.0f;
295 else if(*percent<0) glow = 0.0f;
296 loopobjskins(meshname, s, s.glow = glow);
299 void objglare(char *meshname, float *specglare, float *glowglare)
301 loopobjskins(meshname, s, { s.specglare = *specglare; s.glowglare = *glowglare; });
304 void objalphatest(char *meshname, float *cutoff)
306 loopobjskins(meshname, s, s.alphatest = max(0.0f, min(1.0f, *cutoff)));
309 void objalphablend(char *meshname, int *blend)
311 loopobjskins(meshname, s, s.alphablend = *blend!=0);
314 void objenvmap(char *meshname, char *envmap)
316 Texture *tex = cubemapload(envmap);
317 loopobjskins(meshname, s, s.envmap = tex);
320 void objbumpmap(char *meshname, char *normalmap, char *skin)
322 Texture *normalmaptex = NULL, *skintex = NULL;
323 normalmaptex = textureload(makerelpath(objdir, normalmap, "<noff>"), 0, true, false);
324 if(skin[0]) skintex = textureload(makerelpath(objdir, skin, "<noff>"), 0, true, false);
325 loopobjskins(meshname, s, { s.unlittex = skintex; s.normalmap = normalmaptex; m.calctangents(); });
328 void objtranslucent(char *meshname, float *translucency)
330 loopobjskins(meshname, s, s.translucency = *translucency);
333 void objfullbright(char *meshname, float *fullbright)
335 loopobjskins(meshname, s, s.fullbright = *fullbright);
338 void objshader(char *meshname, char *shader)
340 loopobjskins(meshname, s, s.shader = lookupshaderbyname(shader));
343 void objscroll(char *meshname, float *scrollu, float *scrollv)
345 loopobjskins(meshname, s, { s.scrollu = *scrollu; s.scrollv = *scrollv; });
348 void objnoclip(char *meshname, int *noclip)
350 loopobjmeshes(meshname, m, m.noclip = *noclip!=0);
353 COMMAND(objload, "s");
354 COMMAND(objpitch, "ffff");
355 COMMAND(objskin, "sssff");
356 COMMAND(objspec, "si");
357 COMMAND(objambient, "si");
358 COMMAND(objglow, "si");
359 COMMAND(objglare, "sff");
360 COMMAND(objalphatest, "sf");
361 COMMAND(objalphablend, "si");
362 COMMAND(objenvmap, "ss");
363 COMMAND(objbumpmap, "sss");
364 COMMAND(objtranslucent, "sf");
365 COMMAND(objfullbright, "sf");
366 COMMAND(objshader, "ss");
367 COMMAND(objscroll, "sff");
368 COMMAND(objnoclip, "si");