Line data Source code
1 : /**
2 : * @file vertmodel.cpp
3 : * @brief Vertex model support
4 : *
5 : * Support for vertex animated or static vertex models (as opposed to skeletal models
6 : * in skelmodel)
7 : */
8 : #include "../libprimis-headers/cube.h"
9 : #include "../../shared/geomexts.h"
10 : #include "../../shared/glemu.h"
11 : #include "../../shared/glexts.h"
12 :
13 : #include <format>
14 : #include <memory>
15 : #include <optional>
16 :
17 : #include "interface/console.h"
18 : #include "interface/control.h"
19 :
20 : #include "render/rendergl.h"
21 : #include "render/renderlights.h"
22 : #include "render/rendermodel.h"
23 : #include "render/shader.h"
24 : #include "render/shaderparam.h"
25 : #include "render/texture.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 : //==============================================================================
37 : // vertmodel object
38 : //==============================================================================
39 :
40 0 : vertmodel::meshgroup * vertmodel::loadmeshes(const char *name, float smooth)
41 : {
42 0 : vertmeshgroup *group = newmeshes();
43 0 : if(!group->load(name, smooth))
44 : {
45 0 : delete group;
46 0 : return nullptr;
47 : }
48 0 : return group;
49 : }
50 :
51 0 : vertmodel::meshgroup * vertmodel::sharemeshes(const char *name, float smooth)
52 : {
53 0 : if(meshgroups.find(name) == meshgroups.end())
54 : {
55 0 : meshgroup *group = loadmeshes(name, smooth);
56 0 : if(!group)
57 : {
58 0 : return nullptr;
59 : }
60 0 : meshgroups[group->groupname()] = group;
61 : }
62 0 : return meshgroups[name];
63 : }
64 :
65 0 : vertmodel::vertmodel(std::string name) : animmodel(std::move(name))
66 : {
67 0 : }
68 :
69 : //==============================================================================
70 : // vertmodel::vbocacheentry object
71 : //==============================================================================
72 :
73 0 : vertmodel::vbocacheentry::vbocacheentry() : vbuf(0)
74 : {
75 0 : as.cur.fr1 = as.prev.fr1 = -1;
76 0 : }
77 :
78 : //==============================================================================
79 : // vertmodel::vertmesh object
80 : //==============================================================================
81 :
82 0 : vertmodel::vertmesh::vertmesh() : verts(0), tcverts(0), tris(0)
83 : {
84 0 : }
85 :
86 0 : vertmodel::vertmesh::vertmesh(std::string_view name, meshgroup *m) :
87 : Mesh(name, m),
88 0 : verts(0),
89 0 : tcverts(0),
90 0 : tris(0)
91 : {
92 0 : }
93 :
94 0 : vertmodel::vertmesh::~vertmesh()
95 : {
96 0 : delete[] verts;
97 0 : delete[] tcverts;
98 0 : delete[] tris;
99 0 : }
100 :
101 0 : void vertmodel::vertmesh::smoothnorms(float limit, bool areaweight)
102 : {
103 0 : if((static_cast<vertmeshgroup *>(group))->numframes == 1)
104 : {
105 0 : Mesh::smoothnorms<vertmodel>(verts, numverts, tris, numtris, limit, areaweight);
106 : }
107 : else
108 : {
109 0 : buildnorms(areaweight);
110 : }
111 0 : }
112 :
113 0 : void vertmodel::vertmesh::buildnorms(bool areaweight)
114 : {
115 0 : Mesh::buildnorms<vertmodel>(verts, numverts, tris, numtris, areaweight, (static_cast<vertmeshgroup *>(group))->numframes);
116 0 : }
117 :
118 0 : void vertmodel::vertmesh::calctangents(bool areaweight)
119 : {
120 0 : Mesh::calctangents<vertmodel, vertmodel::tcvert>(verts, tcverts, numverts, tris, numtris, areaweight, (static_cast<vertmeshgroup *>(group))->numframes);
121 0 : }
122 :
123 0 : void vertmodel::vertmesh::calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const
124 : {
125 0 : for(int j = 0; j < numverts; ++j)
126 : {
127 0 : vec v = m.transform(verts[j].pos);
128 0 : bbmin.min(v);
129 0 : bbmax.max(v);
130 : }
131 0 : }
132 :
133 0 : void vertmodel::vertmesh::genBIH(BIH::mesh &m) const
134 : {
135 0 : m.setmesh(reinterpret_cast<const BIH::mesh::tri *>(tris), numtris,
136 0 : reinterpret_cast<const uchar *>(&verts->pos), sizeof(vert),
137 0 : reinterpret_cast<const uchar *>(&tcverts->tc), sizeof(tcvert));
138 0 : }
139 :
140 0 : void vertmodel::vertmesh::genshadowmesh(std::vector<triangle> &out, const matrix4x3 &m) const
141 : {
142 0 : for(int j = 0; j < numtris; ++j)
143 : {
144 0 : out.emplace_back(m.transform(verts[tris[j].vert[0]].pos),
145 0 : m.transform(verts[tris[j].vert[1]].pos),
146 0 : m.transform(verts[tris[j].vert[2]].pos));
147 : }
148 0 : }
149 :
150 0 : void vertmodel::vertmesh::assignvert(vvertg &vv, const tcvert &tc, const vert &v)
151 : {
152 0 : vv.pos = vec4<half>(v.pos, 1);
153 0 : vv.tc = tc.tc;
154 0 : vv.tangent = v.tangent;
155 0 : }
156 :
157 0 : int vertmodel::vertmesh::genvbo(std::vector<uint> &idxs, int offset)
158 : {
159 0 : voffset = offset;
160 0 : for(int i = 0; i < numtris; ++i)
161 : {
162 0 : const tri &t = tris[i];
163 0 : for(int j = 0; j < 3; ++j)
164 : {
165 0 : idxs.push_back(voffset+t.vert[j]);
166 : }
167 : }
168 0 : minvert = voffset;
169 0 : maxvert = voffset + numverts-1;
170 0 : elen = idxs.size();
171 0 : return numverts;
172 : }
173 :
174 0 : void vertmodel::vertmesh::render() const
175 : {
176 0 : if(!Shader::lastshader)
177 : {
178 0 : return;
179 : }
180 0 : glDrawRangeElements(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_INT, nullptr);
181 0 : glde++;
182 0 : xtravertsva += numverts;
183 : }
184 :
185 : //==============================================================================
186 : // vertmodel::vertmeshgroup object
187 : //==============================================================================
188 :
189 0 : vertmodel::vertmeshgroup::vertmeshgroup()
190 0 : : numframes(0), tags(nullptr), numtags(0), ebuf(0), vlen(0), vertsize(0), vdata(nullptr)
191 : {
192 0 : }
193 :
194 0 : vertmodel::vertmeshgroup::~vertmeshgroup()
195 : {
196 0 : delete[] tags;
197 0 : if(ebuf)
198 : {
199 0 : glDeleteBuffers(1, &ebuf);
200 : }
201 0 : for(size_t i = 0; i < maxvbocache; ++i)
202 : {
203 0 : if(vbocache[i].vbuf)
204 : {
205 0 : glDeleteBuffers(1, &vbocache[i].vbuf);
206 : }
207 : }
208 0 : delete[] vdata;
209 0 : }
210 :
211 0 : void vertmodel::vertmeshgroup::concattagtransform(int, const matrix4x3 &, matrix4x3 &) const
212 : {
213 0 : }
214 :
215 0 : std::optional<size_t> vertmodel::vertmeshgroup::findtag(std::string_view name)
216 : {
217 0 : for(size_t i = 0; i < numtags; ++i)
218 : {
219 0 : if(tags[i].name == name)
220 : {
221 0 : return i;
222 : }
223 : }
224 0 : return std::nullopt;
225 : }
226 :
227 0 : bool vertmodel::vertmeshgroup::addtag(std::string_view name, const matrix4x3 &matrix)
228 : {
229 0 : std::optional<size_t> idx = findtag(name);
230 0 : if(idx)
231 : {
232 0 : if(!testtags)
233 : {
234 0 : return false;
235 : }
236 0 : for(int i = 0; i < numframes; ++i)
237 : {
238 0 : tag &t = tags[i*numtags + *idx];
239 0 : t.matrix = matrix;
240 : }
241 : }
242 : else
243 : {
244 0 : tag *newtags = new tag[(numtags+1)*numframes];
245 0 : for(int i = 0; i < numframes; ++i)
246 : {
247 0 : tag *dst = &newtags[(numtags+1)*i],
248 0 : *src = &tags[numtags*i];
249 0 : if(!i)
250 : {
251 0 : for(size_t j = 0; j < numtags; ++j)
252 : {
253 0 : std::swap(dst[j].name, src[j].name);
254 : }
255 0 : dst[numtags].name = name;
256 : }
257 0 : for(size_t j = 0; j < numtags; ++j)
258 : {
259 0 : dst[j].matrix = src[j].matrix;
260 : }
261 0 : dst[numtags].matrix = matrix;
262 : }
263 0 : if(tags)
264 : {
265 0 : delete[] tags;
266 : }
267 0 : tags = newtags;
268 0 : numtags++;
269 : }
270 0 : return true;
271 : }
272 :
273 0 : int vertmodel::vertmeshgroup::totalframes() const
274 : {
275 0 : return numframes;
276 : }
277 :
278 0 : void vertmodel::vertmeshgroup::calctagmatrix(const part *p, int i, const AnimState &as, matrix4 &matrix) const
279 : {
280 0 : const matrix4x3 &tag1 = tags[as.cur.fr1*numtags + i].matrix,
281 0 : &tag2 = tags[as.cur.fr2*numtags + i].matrix;
282 0 : matrix4x3 tag;
283 0 : tag.lerp(tag1, tag2, as.cur.t);
284 0 : if(as.interp<1)
285 : {
286 0 : const matrix4x3 &tag1p = tags[as.prev.fr1*numtags + i].matrix,
287 0 : &tag2p = tags[as.prev.fr2*numtags + i].matrix;
288 0 : matrix4x3 tagp;
289 0 : tagp.lerp(tag1p, tag2p, as.prev.t);
290 0 : tag.lerp(tagp, tag, as.interp);
291 : }
292 0 : tag.d.mul(p->model->locationsize().w * sizescale);
293 0 : matrix = matrix4(tag);
294 0 : }
295 :
296 0 : void vertmodel::vertmeshgroup::genvbo(vbocacheentry &vc)
297 : {
298 0 : if(!vc.vbuf)
299 : {
300 0 : glGenBuffers(1, &vc.vbuf);
301 : }
302 0 : if(ebuf)
303 : {
304 0 : return;
305 : }
306 :
307 0 : std::vector<uint> idxs;
308 0 : std::vector<std::vector<animmodel::Mesh *>::iterator> rendermeshes = getrendermeshes();
309 0 : vlen = 0;
310 0 : if(numframes>1)
311 : {
312 0 : vertsize = sizeof(vvert);
313 :
314 0 : for(std::vector<animmodel::Mesh *>::const_iterator i : rendermeshes)
315 : {
316 0 : vlen += static_cast<vertmesh *>(*i)->genvbo(idxs, vlen);
317 : }
318 0 : delete[] vdata;
319 0 : vdata = new uchar[vlen*vertsize];
320 0 : for(std::vector<animmodel::Mesh *>::const_iterator i : rendermeshes)
321 : {
322 0 : static_cast<vertmesh *>(*i)->fillverts(reinterpret_cast<vvert *>(vdata));
323 : }
324 : }
325 : else
326 : {
327 0 : vertsize = sizeof(vvertg);
328 0 : gle::bindvbo(vc.vbuf);
329 0 : size_t numverts = 0,
330 0 : htlen = 128;
331 0 : for(std::vector<animmodel::Mesh *>::const_iterator i : rendermeshes)
332 : {
333 0 : numverts += static_cast<vertmesh *>(*i)->numverts;
334 : }
335 0 : while(htlen < numverts)
336 : {
337 0 : htlen *= 2;
338 : }
339 0 : if(numverts*4 > htlen*3)
340 : {
341 0 : htlen *= 2;
342 : }
343 0 : int *htdata = new int[htlen];
344 0 : std::memset(htdata, -1, htlen*sizeof(int));
345 0 : std::vector<vvertg> vverts;
346 0 : for(std::vector<animmodel::Mesh *>::const_iterator i : rendermeshes)
347 : {
348 0 : vlen += static_cast<vertmesh *>(*i)->genvbo(idxs, vlen, vverts, htdata, htlen);
349 : }
350 0 : glBufferData(GL_ARRAY_BUFFER, vverts.size()*sizeof(vvertg), vverts.data(), GL_STATIC_DRAW);
351 0 : delete[] htdata;
352 0 : htdata = nullptr;
353 0 : gle::clearvbo();
354 0 : }
355 :
356 0 : glGenBuffers(1, &ebuf);
357 0 : gle::bindebo(ebuf);
358 0 : glBufferData(GL_ELEMENT_ARRAY_BUFFER, idxs.size()*sizeof(uint), idxs.data(), GL_STATIC_DRAW);
359 0 : gle::clearebo();
360 0 : }
361 :
362 0 : void vertmodel::vertmeshgroup::bindvbo(const AnimState *as, const part *p, const vbocacheentry &vc)
363 : {
364 0 : if(numframes>1)
365 : {
366 0 : bindvbo<vvert>(as, p, vc);
367 : }
368 : else
369 : {
370 0 : bindvbo<vvertg>(as, p, vc);
371 : }
372 0 : }
373 :
374 0 : void *vertmodel::vertmeshgroup::animkey()
375 : {
376 0 : return this;
377 : }
378 :
379 0 : void vertmodel::vertmeshgroup::cleanup()
380 : {
381 0 : for(size_t i = 0; i < maxvbocache; ++i)
382 : {
383 0 : vbocacheentry &c = vbocache[i];
384 0 : if(c.vbuf)
385 : {
386 0 : glDeleteBuffers(1, &c.vbuf);
387 0 : c.vbuf = 0;
388 : }
389 0 : c.as.cur.fr1 = -1;
390 : }
391 0 : if(ebuf)
392 : {
393 0 : glDeleteBuffers(1, &ebuf);
394 0 : ebuf = 0;
395 : }
396 0 : }
397 :
398 0 : void vertmodel::vertmeshgroup::preload()
399 : {
400 0 : if(numframes > 1)
401 : {
402 0 : return;
403 : }
404 0 : if(!vbocache[0].vbuf)
405 : {
406 0 : genvbo(vbocache[0]);
407 : }
408 : }
409 :
410 0 : void vertmodel::vertmeshgroup::render(const AnimState *as, float, const vec &, const vec &, dynent *, part *p)
411 : {
412 0 : if(as->cur.anim & Anim_NoRender)
413 : {
414 0 : for(part::linkedpart &l : p->links)
415 : {
416 0 : calctagmatrix(p, l.tag, *as, l.matrix);
417 : }
418 0 : return;
419 : }
420 0 : vbocacheentry *vc = nullptr;
421 0 : if(numframes<=1)
422 : {
423 0 : vc = vbocache.data();
424 : }
425 : else
426 : {
427 0 : for(size_t i = 0; i < maxvbocache; ++i)
428 : {
429 0 : vbocacheentry &c = vbocache[i];
430 0 : if(!c.vbuf)
431 : {
432 0 : continue;
433 : }
434 0 : if(c.as==*as)
435 : {
436 0 : vc = &c;
437 0 : break;
438 : }
439 : }
440 0 : if(!vc)
441 : {
442 0 : for(size_t i = 0; i < maxvbocache; ++i)
443 : {
444 0 : vc = &vbocache[i];
445 0 : if(!vc->vbuf || vc->millis < lastmillis)
446 : {
447 : break;
448 : }
449 : }
450 : }
451 : }
452 0 : if(!vc->vbuf)
453 : {
454 0 : genvbo(*vc);
455 : }
456 0 : if(numframes>1)
457 : {
458 0 : if(vc->as!=*as)
459 : {
460 0 : vc->as = *as;
461 0 : vc->millis = lastmillis;
462 0 : std::vector<std::vector<animmodel::Mesh *>::iterator> rendermeshes = getrendermeshes();
463 0 : for(std::vector<animmodel::Mesh *>::iterator i : rendermeshes)
464 : {
465 0 : static_cast<vertmesh *>(*i)->interpverts(*as, reinterpret_cast<vvert *>(vdata));
466 : }
467 0 : gle::bindvbo(vc->vbuf);
468 0 : glBufferData(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
469 0 : }
470 0 : vc->millis = lastmillis;
471 : }
472 0 : bindvbo(as, p, *vc);
473 0 : LOOP_RENDER_MESHES(vertmesh, m,
474 : {
475 : p->skins[i].bind(m, as); //not a skeletal model, do not need to include trailing params
476 : m.render();
477 : });
478 0 : for(part::linkedpart &l : p->links)
479 : {
480 0 : calctagmatrix(p, l.tag, *as, l.matrix);
481 : }
482 : }
|