Line data Source code
1 : /* obj.cpp: wavefront model support
2 : *
3 : * Libprimis supports the Wavefront (obj) model format for simple static models.
4 : * This file contains the implementation functions, while the class for the obj
5 : * model type is located in obj.h.
6 : */
7 : #include "../libprimis-headers/cube.h"
8 : #include "../../shared/geomexts.h"
9 : #include "../../shared/glemu.h"
10 : #include "../../shared/glexts.h"
11 : #include "../../shared/stream.h"
12 :
13 : #include <optional>
14 : #include <memory>
15 :
16 : #include "render/rendergl.h"
17 : #include "render/rendermodel.h"
18 : #include "render/renderwindow.h"
19 : #include "render/shader.h"
20 : #include "render/shaderparam.h"
21 : #include "render/texture.h"
22 :
23 : #include "interface/console.h"
24 : #include "interface/control.h"
25 : #include "interface/cs.h"
26 :
27 : #include "world/entities.h"
28 : #include "world/octaworld.h"
29 : #include "world/bih.h"
30 :
31 : #include "model.h"
32 : #include "ragdoll.h"
33 : #include "animmodel.h"
34 : #include "vertmodel.h"
35 :
36 : #include "obj.h"
37 :
38 : #include "interface/console.h"
39 :
40 : vertcommands<obj> obj::objcommands;
41 :
42 0 : obj::obj(std::string name) : vertloader(name)
43 : {
44 0 : }
45 :
46 59 : const char *obj::formatname()
47 : {
48 59 : return "obj";
49 : }
50 :
51 1 : bool obj::cananimate()
52 : {
53 1 : return false;
54 : }
55 :
56 0 : bool obj::flipy() const
57 : {
58 0 : return true;
59 : }
60 :
61 0 : int obj::type() const
62 : {
63 0 : return MDL_OBJ;
64 : }
65 :
66 0 : bool obj::skeletal() const
67 : {
68 0 : return false;
69 : }
70 :
71 0 : bool obj::objmeshgroup::load(const char *filename, float smooth)
72 : {
73 0 : int len = std::strlen(filename);
74 0 : if(len < 4 || strcasecmp(&filename[len-4], ".obj"))
75 : {
76 0 : return false;
77 : }
78 0 : stream *file = openfile(filename, "rb");
79 0 : if(!file)
80 : {
81 0 : return false;
82 : }
83 0 : name = filename;
84 0 : numframes = 1;
85 0 : std::array<std::vector<vec>, 3> attrib;
86 : char buf[512];
87 0 : std::unordered_map<ivec, uint> verthash;
88 0 : std::vector<vert> verts;
89 0 : std::vector<tcvert> tcverts;
90 0 : std::vector<tri> tris;
91 :
92 0 : string meshname = "";
93 0 : vertmesh *curmesh = nullptr;
94 0 : while(file->getline(buf, sizeof(buf)))
95 : {
96 0 : char *c = buf;
97 0 : while(std::isspace(*c))
98 : {
99 0 : c++;
100 : }
101 0 : switch(*c)
102 : {
103 0 : case '#':
104 : {
105 0 : continue;
106 : }
107 0 : case 'v':
108 : {
109 0 : if(std::isspace(c[1]))
110 : {
111 0 : parsevert(c, attrib[0]);
112 : }
113 0 : else if(c[1]=='t')
114 : {
115 0 : parsevert(c, attrib[1]);
116 : }
117 0 : else if(c[1]=='n')
118 : {
119 0 : parsevert(c, attrib[2]);
120 : }
121 0 : break;
122 : }
123 0 : case 'g':
124 : {
125 0 : while(std::isalpha(*c))
126 : {
127 0 : c++;
128 : }
129 0 : while(std::isspace(*c))
130 : {
131 0 : c++;
132 : }
133 0 : char *name = c;
134 0 : size_t namelen = std::strlen(name);
135 0 : while(namelen > 0 && std::isspace(name[namelen-1]))
136 : {
137 0 : namelen--;
138 : }
139 0 : copystring(meshname, name, std::min(namelen+1, sizeof(meshname)));
140 0 : if(curmesh)
141 : {
142 0 : flushmesh(curmesh, verts, tcverts, tris, attrib[2], smooth);
143 : }
144 0 : curmesh = nullptr;
145 0 : break;
146 : }
147 0 : case 'f':
148 : {
149 0 : if(!curmesh)
150 : {
151 : //startmesh
152 0 : vertmesh &m = *new vertmesh(meshname[0] ? std::string(meshname) : "", this);
153 0 : meshes.push_back(&m);
154 0 : curmesh = &m;
155 0 : verthash.clear();
156 0 : verts.clear();
157 0 : tcverts.clear();
158 0 : tris.clear();
159 : }
160 0 : std::optional<uint> v0 = std::nullopt,
161 0 : v1 = std::nullopt;
162 0 : while(std::isalpha(*c))
163 : {
164 0 : c++;
165 : }
166 : for(;;)
167 : {
168 0 : while(std::isspace(*c))
169 : {
170 0 : c++;
171 : }
172 0 : if(!*c)
173 : {
174 0 : break;
175 : }
176 0 : ivec vkey(-1, -1, -1);
177 0 : for(int i = 0; i < 3; ++i)
178 : {
179 0 : vkey[i] = std::strtol(c, &c, 10);
180 0 : if(vkey[i] < 0)
181 : {
182 0 : vkey[i] = attrib[i].size() + vkey[i];
183 : }
184 : else
185 : {
186 0 : vkey[i]--;
187 : }
188 0 : if(!(attrib[i].size() > static_cast<uint>(vkey[i])))
189 : {
190 0 : vkey[i] = -1;
191 : }
192 0 : if(*c!='/')
193 : {
194 0 : break;
195 : }
196 0 : c++;
197 : }
198 0 : auto itr = verthash.find(vkey);
199 : uint *index;
200 0 : if(itr == verthash.end())
201 : {
202 0 : index = &verthash[vkey];
203 0 : *index = verts.size();
204 0 : vert v;
205 0 : v.pos = vkey.x < 0 ? vec(0, 0, 0) : attrib[0][vkey.x];
206 0 : v.pos = vec(v.pos.z, -v.pos.x, v.pos.y);
207 0 : v.norm = vkey.z < 0 ? vec(0, 0, 0) : attrib[2][vkey.z];
208 0 : v.norm = vec(v.norm.z, -v.norm.x, v.norm.y);
209 0 : verts.push_back(v);
210 0 : tcverts.push_back({vkey.y < 0 ? vec2(0, 0) : vec2(attrib[1][vkey.y].x, 1-attrib[1][vkey.y].y)});
211 : }
212 : else
213 : {
214 0 : index = &(*itr).second;
215 : }
216 0 : if(!v0)
217 : {
218 0 : v0 = *index;
219 : }
220 0 : else if(!v1)
221 : {
222 0 : v1 = *index;
223 : }
224 : else
225 : {
226 0 : tris.push_back({*index, v1.value(), v0.value()});
227 0 : v1 = *index;
228 : }
229 0 : }
230 0 : break;
231 : }
232 0 : }
233 : }
234 0 : if(curmesh)
235 : {
236 0 : flushmesh(curmesh, verts, tcverts, tris, attrib[2], smooth);
237 : }
238 0 : delete file;
239 0 : return true;
240 0 : }
241 :
242 0 : void obj::objmeshgroup::parsevert(char *s, std::vector<vec> &out)
243 : {
244 0 : out.emplace_back(vec(0, 0, 0));
245 0 : vec &v = out.back();
246 0 : while(std::isalpha(*s))
247 : {
248 0 : s++;
249 : }
250 0 : for(int i = 0; i < 3; ++i)
251 : {
252 0 : v[i] = std::strtod(s, &s);
253 0 : while(std::isspace(*s))
254 : {
255 0 : s++;
256 : }
257 0 : if(!*s)
258 : {
259 0 : break;
260 : }
261 : }
262 0 : }
263 :
264 0 : void obj::objmeshgroup::flushmesh(vertmesh *curmesh,
265 : const std::vector<vert> &verts,
266 : const std::vector<tcvert> &tcverts,
267 : const std::vector<tri> &tris,
268 : const std::vector<vec> &attrib,
269 : float smooth)
270 : {
271 0 : curmesh->numverts = verts.size();
272 0 : if(verts.size())
273 : {
274 0 : curmesh->verts = new vert[verts.size()];
275 0 : std::memcpy(curmesh->verts, verts.data(), verts.size()*sizeof(vert));
276 0 : curmesh->tcverts = new tcvert[verts.size()];
277 0 : std::memcpy(curmesh->tcverts, tcverts.data(), tcverts.size()*sizeof(tcvert));
278 : }
279 0 : curmesh->numtris = tris.size();
280 0 : if(tris.size())
281 : {
282 0 : curmesh->tris = new tri[tris.size()];
283 0 : std::memcpy(curmesh->tris, tris.data(), tris.size()*sizeof(tri));
284 : }
285 0 : if(attrib.empty())
286 : {
287 0 : if(smooth <= 1)
288 : {
289 0 : curmesh->smoothnorms(smooth);
290 : }
291 : else
292 : {
293 0 : curmesh->buildnorms();
294 : }
295 : }
296 0 : curmesh->calctangents();
297 0 : }
298 :
299 0 : bool obj::loaddefaultparts()
300 : {
301 0 : part &mdl = addpart();
302 0 : std::string pname = parentdir(modelname().c_str());
303 0 : DEF_FORMAT_STRING(name1, "%s%s/tris.obj", modelpath.c_str(), modelname().c_str());
304 0 : mdl.meshes = sharemeshes(path(name1));
305 0 : if(!mdl.meshes)
306 : {
307 0 : DEF_FORMAT_STRING(name2, "%s%s/tris.obj", modelpath.c_str(), pname.c_str()); // try obj in parent folder (vert sharing)
308 0 : mdl.meshes = sharemeshes(path(name2));
309 0 : if(!mdl.meshes)
310 : {
311 0 : return false;
312 : }
313 : }
314 : Texture *tex, *masks;
315 0 : loadskin(modelname(), pname, tex, masks);
316 0 : mdl.initskins(tex, masks);
317 0 : if(tex==notexture)
318 : {
319 0 : conoutf("could not load model skin for %s", name1);
320 : }
321 0 : return true;
322 0 : }
|