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
++;
22 while(isspace(*s
)) s
++;
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
);
39 vector
<vec
> attrib
[3];
42 hashtable
<ivec
, int> verthash
;
44 vector
<tcvert
> tcverts
;
47 #define STARTMESH do { \
48 vertmesh &m = *new vertmesh; \
50 m.name = meshname[0] ? newstring(meshname) : NULL; \
54 verts.setsizenodelete(0); \
55 tcverts.setsizenodelete(0); \
56 tris.setsizenodelete(0); \
59 #define FLUSHMESH do { \
60 curmesh->numverts = verts.length(); \
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)); \
68 curmesh->numtris = tris.length(); \
71 curmesh->tris = new tri[tris.length()]; \
72 memcpy(curmesh->tris, tris.getbuf(), tris.length()*sizeof(tri)); \
77 vertmesh
*curmesh
= NULL
;
80 fgets(buf
, sizeof(buf
), file
);
83 while(isspace(*c
)) c
++;
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]);
94 while(isalpha(*c
)) c
++;
95 while(isspace(*c
)) 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
;
107 if(!curmesh
) STARTMESH
;
108 int v0
= -1, v1
= -1;
109 while(isalpha(*c
)) c
++;
112 while(isspace(*c
)) c
++;
114 ivec
vkey(-1, -1, -1);
117 vkey
[i
] = strtol(c
, &c
, 10);
118 if(vkey
[i
] < 0) vkey
[i
] = attrib
[i
].length() - vkey
[i
];
120 if(!attrib
[i
].inrange(vkey
[i
])) vkey
[i
] = -1;
124 int *index
= verthash
.access(vkey
);
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
;
141 t
.vert
[0] = ushort(v0
);
142 t
.vert
[1] = ushort(v1
);
143 t
.vert
[2] = ushort(*index
);
152 if(curmesh
) FLUSHMESH
;
160 meshgroup
*loadmeshes(char *name
, va_list args
)
162 objmeshgroup
*group
= new objmeshgroup
;
163 if(!group
->load(name
)) { delete group
; return NULL
; }
167 bool loaddefaultparts()
169 part
&mdl
= *new part
;
173 const char *pname
= parentdir(loadname
);
174 s_sprintfd(name1
)("packages/models/%s/tris.obj", loadname
);
175 mdl
.meshes
= sharemeshes(path(name1
));
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
);
191 if(loaded
) return true;
192 s_sprintf(objdir
)("packages/models/%s", loadname
);
193 s_sprintfd(cfgname
)("packages/models/%s/obj.cfg", loadname
);
196 persistidents
= false;
197 if(execfile(cfgname
) && parts
.length()) // configured obj, will call the obj* commands below
199 persistidents
= true;
201 loopv(parts
) if(!parts
[i
]->meshes
) return false;
203 else // obj without configuration, try default tris and skin
205 persistidents
= true;
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
;
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))) \
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);
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
)
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
)
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");