Line data Source code
1 : #ifndef VERTMODEL_H_
2 : #define VERTMODEL_H_
3 :
4 : class vertmodel : public animmodel
5 : {
6 : public:
7 : struct vert final
8 : {
9 : vec pos, norm;
10 : vec4<float> tangent;
11 : };
12 :
13 : struct vvert final
14 : {
15 : vec pos;
16 : GenericVec2<half> tc;
17 : squat tangent;
18 : };
19 :
20 : struct vvertg final
21 : {
22 : vec4<half> pos;
23 : GenericVec2<half> tc;
24 : squat tangent;
25 : };
26 :
27 : struct tcvert final
28 : {
29 : vec2 tc;
30 : };
31 :
32 : struct tri final
33 : {
34 : uint vert[3];
35 : };
36 :
37 : struct vbocacheentry final
38 : {
39 : GLuint vbuf;
40 : AnimState as;
41 : int millis;
42 :
43 : vbocacheentry();
44 : };
45 :
46 : struct vertmesh : Mesh
47 : {
48 : public:
49 : vert *verts;
50 : tcvert *tcverts;
51 : tri *tris;
52 : int numverts, numtris;
53 :
54 : vertmesh();
55 : vertmesh(std::string_view name, meshgroup *m);
56 :
57 : virtual ~vertmesh();
58 :
59 : void smoothnorms(float limit = 0, bool areaweight = true);
60 : void buildnorms(bool areaweight = true);
61 : void calctangents(bool areaweight = true);
62 : void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const override final;
63 : void genBIH(BIH::mesh &m) const override final;
64 : void genshadowmesh(std::vector<triangle> &out, const matrix4x3 &m) const override final;
65 :
66 : static void assignvert(vvertg &vv, const tcvert &tc, const vert &v);
67 :
68 : template<class T>
69 0 : int genvbo(std::vector<uint> &idxs, int offset, std::vector<T> &vverts, int *htdata, int htlen)
70 : {
71 0 : voffset = offset;
72 0 : minvert = UINT_MAX;
73 0 : for(int i = 0; i < numtris; ++i)
74 : {
75 0 : const tri &t = tris[i];
76 0 : for(int j = 0; j < 3; ++j)
77 : {
78 0 : const int index = t.vert[j];
79 0 : const vert &v = verts[index];
80 0 : const tcvert &tc = tcverts[index];
81 0 : T vv;
82 0 : assignvert(vv, tc, v);
83 : const auto hashfn = std::hash<vec>();
84 0 : const int htidx = hashfn(v.pos)&(htlen-1);
85 0 : for(int k = 0; k < htlen; ++k)
86 : {
87 0 : int &vidx = htdata[(htidx+k)&(htlen-1)];
88 0 : if(vidx < 0)
89 : {
90 0 : idxs.push_back(vverts.size());
91 0 : vidx = idxs.back();
92 0 : vverts.push_back(vv);
93 0 : break;
94 : }
95 0 : else if(!std::memcmp(&vverts[vidx], &vv, sizeof(vv)))
96 : {
97 0 : idxs.push_back(static_cast<uint>(vidx));
98 0 : minvert = std::min(minvert, idxs.back());
99 0 : break;
100 : }
101 : }
102 : }
103 : }
104 0 : minvert = std::min(minvert, static_cast<uint>(voffset));
105 0 : maxvert = std::max(minvert, static_cast<uint>(vverts.size()-1));
106 0 : elen = idxs.size();
107 0 : return vverts.size()-voffset;
108 : }
109 :
110 : int genvbo(std::vector<uint> &idxs, int offset);
111 :
112 : template<class T>
113 0 : static void fillvert(T &vv, tcvert &tc)
114 : {
115 0 : vv.tc = tc.tc;
116 0 : }
117 :
118 : template<class T>
119 0 : void fillverts(T *vdata)
120 : {
121 0 : vdata += voffset;
122 0 : for(int i = 0; i < numverts; ++i)
123 : {
124 0 : fillvert(vdata[i], tcverts[i]);
125 : }
126 0 : }
127 :
128 : template<class T>
129 0 : void interpverts(const AnimState &as, T * RESTRICT vdata)
130 : {
131 0 : vdata += voffset;
132 0 : const vert * RESTRICT vert1 = &verts[as.cur.fr1 * numverts],
133 0 : * RESTRICT vert2 = &verts[as.cur.fr2 * numverts],
134 0 : * RESTRICT pvert1 = as.interp<1 ? &verts[as.prev.fr1 * numverts] : nullptr,
135 0 : * RESTRICT pvert2 = as.interp<1 ? &verts[as.prev.fr2 * numverts] : nullptr;
136 0 : if(as.interp<1)
137 : {
138 0 : for(int i = 0; i < numverts; ++i)
139 : {
140 0 : T &v = vdata[i];
141 0 : v.pos.lerp(vec().lerp(pvert1[i].pos, pvert2[i].pos, as.prev.t),
142 0 : vec().lerp(vert1[i].pos, vert2[i].pos, as.cur.t),
143 0 : as.interp);
144 0 : v.tangent.lerp(vec4<float>().lerp(pvert1[i].tangent, pvert2[i].tangent, as.prev.t),
145 0 : vec4<float>().lerp(vert1[i].tangent, vert2[i].tangent, as.cur.t),
146 0 : as.interp);
147 : }
148 : }
149 : else
150 : {
151 0 : for(int i = 0; i < numverts; ++i)
152 : {
153 0 : T &v = vdata[i];
154 0 : v.pos.lerp(vert1[i].pos, vert2[i].pos, as.cur.t);
155 0 : v.tangent.lerp(vert1[i].tangent, vert2[i].tangent, as.cur.t);
156 : }
157 : }
158 0 : }
159 :
160 : /**
161 : * @brief Draws the elements in this vertmesh.
162 : *
163 : * Fails to draw if there is no shader assigned to Shader::lastshader.
164 : *
165 : * Increments glde by one and the xtravertsva counter by the number of vertices drawn.
166 : */
167 : void render() const;
168 : private:
169 : int voffset, elen;
170 : uint minvert, maxvert;
171 : };
172 :
173 : struct tag final
174 : {
175 : std::string name;
176 : matrix4x3 matrix;
177 :
178 0 : tag() {}
179 : };
180 :
181 : struct vertmeshgroup : meshgroup
182 : {
183 : int numframes;
184 : //size equal to numtags * numframes
185 : //tags are ordered beneath frames; tag1frame1, tag2frame1, tag3frame1, tag1frame2, etc
186 : //deleted and re-created whenever a new tag is added with addtag();
187 : tag *tags;
188 : size_t numtags;
189 :
190 : static constexpr size_t maxvbocache = 16;
191 : std::array<vbocacheentry, 16> vbocache;
192 :
193 : GLuint ebuf;
194 : int vlen, vertsize;
195 : uchar *vdata;
196 :
197 : vertmeshgroup();
198 : virtual ~vertmeshgroup();
199 :
200 : virtual void concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const override final;
201 : bool addtag(std::string_view name, const matrix4x3 &matrix);
202 : std::optional<size_t> findtag(std::string_view name) override final;
203 :
204 : /**
205 : * @brief Returns the number of frames associated with this model.
206 : *
207 : * This is the numframes field in the meshgroup object.
208 : */
209 : int totalframes() const override final;
210 : void calctagmatrix(const part *p, int i, const AnimState &as, matrix4 &matrix) const;
211 :
212 : void genvbo(vbocacheentry &vc);
213 :
214 : template<class T>
215 0 : void bindvbo(const AnimState *as, const part *p, const vbocacheentry &vc)
216 : {
217 0 : const T *vverts = 0;
218 0 : bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
219 0 : if(as->cur.anim & Anim_NoSkin)
220 : {
221 0 : if(enabletangents)
222 : {
223 0 : disabletangents();
224 : }
225 0 : if(p->alphatested())
226 : {
227 0 : bindtc(&vverts->tc, vertsize);
228 : }
229 0 : else if(enabletc)
230 : {
231 0 : disabletc();
232 : }
233 : }
234 : else
235 : {
236 0 : bindtangents(&vverts->tangent, vertsize);
237 0 : bindtc(&vverts->tc, vertsize);
238 : }
239 0 : if(enablebones)
240 : {
241 0 : disablebones();
242 : }
243 0 : }
244 :
245 : void bindvbo(const AnimState *as, const part *p, const vbocacheentry &vc);
246 : /**
247 : * @brief Returns a pointer to this object.
248 : *
249 : * There is no appropriate pointer to pass other than itself, since
250 : * this is not a skeletal model
251 : */
252 : void *animkey() override final;
253 : void cleanup() override final;
254 : void preload() override final;
255 : void render(const AnimState *as, float, const vec &, const vec &, dynent *, part *p) override final;
256 :
257 : virtual bool load(const char *name, float smooth) = 0;
258 : };
259 :
260 : virtual vertmeshgroup *newmeshes() = 0;
261 :
262 : meshgroup *sharemeshes(const char *name, float smooth = 2);
263 :
264 : vertmodel(std::string name);
265 :
266 : private:
267 : meshgroup *loadmeshes(const char *name, float smooth = 2);
268 :
269 : };
270 :
271 : template<class MDL>
272 : struct vertloader : modelloader<MDL, vertmodel>
273 : {
274 0 : vertloader(std::string name) : modelloader<MDL, vertmodel>(name) {}
275 : };
276 :
277 : template<class MDL>
278 : struct vertcommands : modelcommands<MDL>
279 : {
280 : typedef struct MDL::vertmeshgroup meshgroup;
281 : typedef class MDL::part part;
282 : typedef class MDL::skin skin;
283 :
284 1 : static void loadpart(const char *model, const float *smooth)
285 : {
286 1 : if(!MDL::loading)
287 : {
288 1 : conoutf("not loading an %s", MDL::formatname());
289 1 : return;
290 : }
291 0 : std::string filename = std::format("{}/{}", MDL::dir, model);
292 0 : part &mdl = MDL::loading->addpart();
293 0 : if(mdl.index)
294 : {
295 0 : mdl.disablepitch();
296 : }
297 0 : mdl.meshes = MDL::loading->sharemeshes(path(filename).c_str(), *smooth > 0 ? std::cos(std::clamp(*smooth, 0.0f, 180.0f)/RAD) : 2);
298 0 : if(!mdl.meshes)
299 : {
300 0 : conoutf("could not load %s", filename.c_str());
301 : }
302 : else
303 : {
304 0 : mdl.initskins();
305 : }
306 0 : }
307 :
308 1 : static void settag(const char *tagname, const float *tx, const float *ty, const float *tz, const float *rx, const float *ry, const float *rz)
309 : {
310 1 : if(!MDL::loading || MDL::loading->parts.empty())
311 : {
312 1 : conoutf("not loading an %s", MDL::formatname());
313 1 : return;
314 : }
315 0 : const part &mdl = *static_cast<part *>(MDL::loading->parts.back());
316 0 : float cx = *rx ? std::cos(*rx/(2*RAD)) : 1,
317 0 : sx = *rx ? std::sin(*rx/(2*RAD)) : 0,
318 0 : cy = *ry ? std::cos(*ry/(2*RAD)) : 1,
319 0 : sy = *ry ? std::sin(*ry/(2*RAD)) : 0,
320 0 : cz = *rz ? std::cos(*rz/(2*RAD)) : 1,
321 0 : sz = *rz ? std::sin(*rz/(2*RAD)) : 0;
322 : //matrix m created from (matrix3 created from quat) + (vec) appended afterwards
323 0 : matrix4x3 m(static_cast<matrix3>(quat(sx*cy*cz - cx*sy*sz,
324 0 : cx*sy*cz + sx*cy*sz,
325 0 : cx*cy*sz - sx*sy*cz,
326 0 : cx*cy*cz + sx*sy*sz)),
327 0 : vec(*tx, *ty, *tz));
328 :
329 0 : static_cast<meshgroup *>(mdl.meshes)->addtag(tagname, m);
330 : }
331 :
332 0 : static void setpitch(const float *pitchscale, const float *pitchoffset, const float *pitchmin, const float *pitchmax)
333 : {
334 0 : if(!MDL::loading || MDL::loading->parts.empty())
335 : {
336 0 : conoutf("not loading an %s", MDL::formatname());
337 0 : return;
338 : }
339 0 : part &mdl = *MDL::loading->parts.back();
340 0 : mdl.pitchscale = *pitchscale;
341 0 : mdl.pitchoffset = *pitchoffset;
342 0 : if(*pitchmin || *pitchmax)
343 : {
344 0 : mdl.pitchmin = *pitchmin;
345 0 : mdl.pitchmax = *pitchmax;
346 : }
347 : else
348 : {
349 0 : mdl.pitchmin = -360*std::fabs(mdl.pitchscale) + mdl.pitchoffset;
350 0 : mdl.pitchmax = 360*std::fabs(mdl.pitchscale) + mdl.pitchoffset;
351 : }
352 : }
353 :
354 0 : static void setanim(const char *anim, const int *frame, const int *range, const float *speed, const int *priority)
355 : {
356 0 : if(!MDL::loading || MDL::loading->parts.empty())
357 : {
358 0 : conoutf("not loading an %s", MDL::formatname());
359 0 : return;
360 : }
361 0 : std::vector<size_t> anims = findanims(anim);
362 0 : if(anims.empty())
363 : {
364 0 : conoutf("could not find animation %s", anim);
365 : }
366 : else
367 : {
368 0 : for(const size_t &i : anims)
369 : {
370 0 : MDL::loading->parts.back()->setanim(0, i, *frame, *range, *speed, *priority);
371 : }
372 : }
373 0 : }
374 :
375 1 : vertcommands()
376 1 : {
377 1 : if(MDL::multiparted())
378 : {
379 1 : this->modelcommand(loadpart, "load", "sf"); //<fmt>load [model] [smooth]
380 : }
381 1 : this->modelcommand(settag, "tag", "sffffff"); //<fmt>tag [tagname] [tx] [ty] [tz] [rx] [ry] [rz]
382 1 : this->modelcommand(setpitch, "pitch", "ffff"); //<fmt>pitch [scale] [offset] [min] [max]
383 1 : if(MDL::cananimate())
384 : {
385 0 : this->modelcommand(setanim, "anim", "siiff"); //<fmt>anim [anim] [frame] [range] [speed] [priority]
386 : }
387 1 : }
388 : };
389 :
390 : #endif
|