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