Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
3 | * described in COPYING.txt. |
||
4 | * Portions of this file are copyright Parallax Software and licensed |
||
5 | * according to the Parallax license below. |
||
6 | * See COPYING.txt for license details. |
||
7 | |||
8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
17 | COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * |
||
22 | * Hacked-in polygon objects |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | |||
27 | #include <stdio.h> |
||
28 | #include <stdlib.h> |
||
29 | #include <string.h> |
||
30 | |||
31 | #include "inferno.h" |
||
32 | #include "robot.h" |
||
33 | #include "vecmat.h" |
||
34 | #include "cntrlcen.h" |
||
35 | #include "interp.h" |
||
36 | #include "dxxerror.h" |
||
37 | #include "u_mem.h" |
||
38 | #include "args.h" |
||
39 | #include "physfs-serial.h" |
||
40 | #include "physfsx.h" |
||
41 | #ifndef DRIVE |
||
42 | #include "texmap.h" |
||
43 | #include "bm.h" |
||
44 | #include "textures.h" |
||
45 | #include "object.h" |
||
46 | #include "lighting.h" |
||
47 | #include "piggy.h" |
||
48 | #endif |
||
49 | #include "render.h" |
||
50 | #if DXX_USE_OGL |
||
51 | #include "ogl_init.h" |
||
52 | #endif |
||
53 | #include "bm.h" |
||
54 | |||
55 | #include "d_zip.h" |
||
56 | #include "partial_range.h" |
||
57 | #include <memory> |
||
58 | |||
59 | namespace dcx { |
||
60 | unsigned N_polygon_models = 0; |
||
61 | } |
||
62 | |||
63 | #define PM_COMPATIBLE_VERSION 6 |
||
64 | #define PM_OBJFILE_VERSION 8 |
||
65 | |||
66 | static unsigned Pof_file_end; |
||
67 | static unsigned Pof_addr; |
||
68 | |||
69 | #define MODEL_BUF_SIZE 32768 |
||
70 | |||
71 | static void _pof_cfseek(int len,int type) |
||
72 | { |
||
73 | switch (type) { |
||
74 | case SEEK_SET: Pof_addr = len; break; |
||
75 | case SEEK_CUR: Pof_addr += len; break; |
||
76 | case SEEK_END: |
||
77 | Assert(len <= 0); // seeking from end, better be moving back. |
||
78 | Pof_addr = Pof_file_end + len; |
||
79 | break; |
||
80 | } |
||
81 | |||
82 | if (Pof_addr > MODEL_BUF_SIZE) |
||
83 | Int3(); |
||
84 | } |
||
85 | |||
86 | #define pof_cfseek(_buf,_len,_type) _pof_cfseek((_len),(_type)) |
||
87 | |||
88 | static int pof_read_int(ubyte *bufp) |
||
89 | { |
||
90 | int i; |
||
91 | |||
92 | i = *(reinterpret_cast<int *>(&bufp[Pof_addr])); |
||
93 | Pof_addr += 4; |
||
94 | return INTEL_INT(i); |
||
95 | |||
96 | // if (PHYSFS_read(f,&i,sizeof(i),1) != 1) |
||
97 | // Error("Unexpected end-of-file while reading object"); |
||
98 | // |
||
99 | // return i; |
||
100 | } |
||
101 | |||
102 | static size_t pof_cfread(void *dst, size_t elsize, size_t nelem, ubyte *bufp) |
||
103 | { |
||
104 | if (Pof_addr + nelem*elsize > Pof_file_end) |
||
105 | return 0; |
||
106 | |||
107 | memcpy(dst, &bufp[Pof_addr], elsize*nelem); |
||
108 | |||
109 | Pof_addr += elsize*nelem; |
||
110 | |||
111 | if (Pof_addr > MODEL_BUF_SIZE) |
||
112 | Int3(); |
||
113 | |||
114 | return nelem; |
||
115 | } |
||
116 | |||
117 | // #define new_read_int(i,f) PHYSFS_read((f),&(i),sizeof(i),1) |
||
118 | #define new_pof_read_int(i,f) pof_cfread(&(i),sizeof(i),1,(f)) |
||
119 | |||
120 | static short pof_read_short(ubyte *bufp) |
||
121 | { |
||
122 | short s; |
||
123 | |||
124 | s = *(reinterpret_cast<int16_t *>(&bufp[Pof_addr])); |
||
125 | Pof_addr += 2; |
||
126 | return INTEL_SHORT(s); |
||
127 | // if (PHYSFS_read(f,&s,sizeof(s),1) != 1) |
||
128 | // Error("Unexpected end-of-file while reading object"); |
||
129 | // |
||
130 | // return s; |
||
131 | } |
||
132 | |||
133 | static void pof_read_string(char *buf,int max_char, ubyte *bufp) |
||
134 | { |
||
135 | for (int i=0; i<max_char; i++) { |
||
136 | if ((*buf++ = bufp[Pof_addr++]) == 0) |
||
137 | break; |
||
138 | } |
||
139 | |||
140 | // while (max_char-- && (*buf=PHYSFSX_fgetc(f)) != 0) buf++; |
||
141 | |||
142 | } |
||
143 | |||
144 | static void pof_read_vecs(vms_vector *vecs,int n,ubyte *bufp) |
||
145 | { |
||
146 | // PHYSFS_read(f,vecs,sizeof(vms_vector),n); |
||
147 | for (int i = 0; i < n; i++) |
||
148 | { |
||
149 | vecs[i].x = pof_read_int(bufp); |
||
150 | vecs[i].y = pof_read_int(bufp); |
||
151 | vecs[i].z = pof_read_int(bufp); |
||
152 | } |
||
153 | |||
154 | if (Pof_addr > MODEL_BUF_SIZE) |
||
155 | Int3(); |
||
156 | } |
||
157 | |||
158 | static void pof_read_angs(vms_angvec *angs,int n,ubyte *bufp) |
||
159 | { |
||
160 | for (int i = 0; i < n; i++) |
||
161 | { |
||
162 | angs[i].p = pof_read_short(bufp); |
||
163 | angs[i].b = pof_read_short(bufp); |
||
164 | angs[i].h = pof_read_short(bufp); |
||
165 | } |
||
166 | |||
167 | if (Pof_addr > MODEL_BUF_SIZE) |
||
168 | Int3(); |
||
169 | } |
||
170 | |||
171 | #define ID_OHDR 0x5244484f // 'RDHO' //Object header |
||
172 | #define ID_SOBJ 0x4a424f53 // 'JBOS' //Subobject header |
||
173 | #define ID_GUNS 0x534e5547 // 'SNUG' //List of guns on this object |
||
174 | #define ID_ANIM 0x4d494e41 // 'MINA' //Animation data |
||
175 | #define ID_IDTA 0x41544449 // 'ATDI' //Interpreter data |
||
176 | #define ID_TXTR 0x52545854 // 'RTXT' //Texture filename list |
||
177 | |||
178 | static std::array<std::array<vms_angvec, MAX_SUBMODELS>, N_ANIM_STATES> anim_angs; |
||
179 | |||
180 | //set the animation angles for this robot. Gun fields of robot info must |
||
181 | //be filled in. |
||
182 | |||
183 | //reads a binary file containing a 3d model |
||
184 | static polymodel *read_model_file(polymodel *pm,const char *filename,robot_info *r) |
||
185 | { |
||
186 | short version; |
||
187 | int len, next_chunk; |
||
188 | ubyte model_buf[MODEL_BUF_SIZE]; |
||
189 | |||
190 | auto ifile = PHYSFSX_openReadBuffered(filename); |
||
191 | if (!ifile) |
||
192 | Error("Can't open file <%s>",filename); |
||
193 | |||
194 | Assert(PHYSFS_fileLength(ifile) <= MODEL_BUF_SIZE); |
||
195 | |||
196 | Pof_addr = 0; |
||
197 | Pof_file_end = PHYSFS_read(ifile, model_buf, 1, PHYSFS_fileLength(ifile)); |
||
198 | ifile.reset(); |
||
199 | const int model_id = pof_read_int(model_buf); |
||
200 | |||
201 | if (model_id != 0x4f505350) /* 'OPSP' */ |
||
202 | Error("Bad ID in model file <%s>",filename); |
||
203 | |||
204 | version = pof_read_short(model_buf); |
||
205 | |||
206 | if (version < PM_COMPATIBLE_VERSION || version > PM_OBJFILE_VERSION) |
||
207 | Error("Bad version (%d) in model file <%s>",version,filename); |
||
208 | |||
209 | int pof_id; |
||
210 | while (new_pof_read_int(pof_id, model_buf) == 1) |
||
211 | { |
||
212 | pof_id = INTEL_INT(pof_id); |
||
213 | //id = pof_read_int(model_buf); |
||
214 | len = pof_read_int(model_buf); |
||
215 | next_chunk = Pof_addr + len; |
||
216 | |||
217 | switch (pof_id) |
||
218 | { |
||
219 | case ID_OHDR: { //Object header |
||
220 | vms_vector pmmin,pmmax; |
||
221 | |||
222 | pm->n_models = pof_read_int(model_buf); |
||
223 | pm->rad = pof_read_int(model_buf); |
||
224 | |||
225 | Assert(pm->n_models <= MAX_SUBMODELS); |
||
226 | |||
227 | pof_read_vecs(&pmmin,1,model_buf); |
||
228 | pof_read_vecs(&pmmax,1,model_buf); |
||
229 | |||
230 | break; |
||
231 | } |
||
232 | |||
233 | case ID_SOBJ: { //Subobject header |
||
234 | int n; |
||
235 | |||
236 | n = pof_read_short(model_buf); |
||
237 | |||
238 | Assert(n < MAX_SUBMODELS); |
||
239 | |||
240 | pm->submodel_parents[n] = pof_read_short(model_buf); |
||
241 | |||
242 | pof_read_vecs(&pm->submodel_norms[n],1,model_buf); |
||
243 | pof_read_vecs(&pm->submodel_pnts[n],1,model_buf); |
||
244 | pof_read_vecs(&pm->submodel_offsets[n],1,model_buf); |
||
245 | |||
246 | pm->submodel_rads[n] = pof_read_int(model_buf); //radius |
||
247 | |||
248 | pm->submodel_ptrs[n] = pof_read_int(model_buf); //offset |
||
249 | |||
250 | break; |
||
251 | |||
252 | } |
||
253 | |||
254 | #ifndef DRIVE |
||
255 | case ID_GUNS: { //List of guns on this object |
||
256 | |||
257 | if (r) { |
||
258 | vms_vector gun_dir; |
||
259 | |||
260 | r->n_guns = pof_read_int(model_buf); |
||
261 | |||
262 | Assert(r->n_guns <= MAX_GUNS); |
||
263 | |||
264 | for (int i=0;i<r->n_guns;i++) { |
||
265 | const uint_fast32_t gun_id = pof_read_short(model_buf); |
||
266 | /* |
||
267 | * D1 v1.0 boss02.pof has id=4 and r->n_guns==4. |
||
268 | * Relax the assert to check only for memory |
||
269 | * corruption. |
||
270 | */ |
||
271 | assert(gun_id < std::size(r->gun_submodels)); |
||
272 | auto &submodel = r->gun_submodels[gun_id]; |
||
273 | submodel = pof_read_short(model_buf); |
||
274 | Assert(submodel != 0xff); |
||
275 | pof_read_vecs(&r->gun_points[gun_id], 1, model_buf); |
||
276 | |||
277 | if (version >= 7) |
||
278 | pof_read_vecs(&gun_dir,1,model_buf); |
||
279 | } |
||
280 | } |
||
281 | else |
||
282 | pof_cfseek(model_buf,len,SEEK_CUR); |
||
283 | |||
284 | break; |
||
285 | } |
||
286 | |||
287 | case ID_ANIM: //Animation data |
||
288 | if (r) { |
||
289 | unsigned n_frames; |
||
290 | |||
291 | n_frames = pof_read_short(model_buf); |
||
292 | |||
293 | Assert(n_frames == N_ANIM_STATES); |
||
294 | |||
295 | for (int m=0;m<pm->n_models;m++) |
||
296 | range_for (auto &f, partial_range(anim_angs, n_frames)) |
||
297 | pof_read_angs(&f[m], 1, model_buf); |
||
298 | |||
299 | |||
300 | robot_set_angles(r,pm,anim_angs); |
||
301 | |||
302 | } |
||
303 | else |
||
304 | pof_cfseek(model_buf,len,SEEK_CUR); |
||
305 | |||
306 | break; |
||
307 | #endif |
||
308 | |||
309 | case ID_TXTR: { //Texture filename list |
||
310 | int n; |
||
311 | char name_buf[128]; |
||
312 | |||
313 | n = pof_read_short(model_buf); |
||
314 | while (n--) { |
||
315 | pof_read_string(name_buf,128,model_buf); |
||
316 | } |
||
317 | |||
318 | break; |
||
319 | } |
||
320 | |||
321 | case ID_IDTA: //Interpreter data |
||
322 | pm->model_data_size = len; |
||
323 | pm->model_data = std::make_unique<uint8_t[]>(pm->model_data_size); |
||
324 | |||
325 | pof_cfread(pm->model_data.get(),1,len,model_buf); |
||
326 | |||
327 | break; |
||
328 | |||
329 | default: |
||
330 | pof_cfseek(model_buf,len,SEEK_CUR); |
||
331 | break; |
||
332 | |||
333 | } |
||
334 | if ( version >= 8 ) // Version 8 needs 4-byte alignment!!! |
||
335 | pof_cfseek(model_buf,next_chunk,SEEK_SET); |
||
336 | } |
||
337 | |||
338 | #if DXX_WORDS_NEED_ALIGNMENT |
||
339 | align_polygon_model_data(pm); |
||
340 | #endif |
||
341 | if constexpr (words_bigendian) |
||
342 | swap_polygon_model_data(pm->model_data.get()); |
||
343 | return pm; |
||
344 | } |
||
345 | |||
346 | //reads the gun information for a model |
||
347 | //fills in arrays gun_points & gun_dirs, returns the number of guns read |
||
348 | void read_model_guns(const char *filename, reactor &r) |
||
349 | { |
||
350 | auto &gun_points = r.gun_points; |
||
351 | auto &gun_dirs = r.gun_dirs; |
||
352 | short version; |
||
353 | int len; |
||
354 | int n_guns=0; |
||
355 | ubyte model_buf[MODEL_BUF_SIZE]; |
||
356 | |||
357 | auto ifile = PHYSFSX_openReadBuffered(filename); |
||
358 | if (!ifile) |
||
359 | Error("Can't open file <%s>",filename); |
||
360 | |||
361 | Assert(PHYSFS_fileLength(ifile) <= MODEL_BUF_SIZE); |
||
362 | |||
363 | Pof_addr = 0; |
||
364 | Pof_file_end = PHYSFS_read(ifile, model_buf, 1, PHYSFS_fileLength(ifile)); |
||
365 | ifile.reset(); |
||
366 | |||
367 | const int model_id = pof_read_int(model_buf); |
||
368 | |||
369 | if (model_id != 0x4f505350) /* 'OPSP' */ |
||
370 | Error("Bad ID in model file <%s>",filename); |
||
371 | |||
372 | version = pof_read_short(model_buf); |
||
373 | |||
374 | Assert(version >= 7); //must be 7 or higher for this data |
||
375 | |||
376 | if (version < PM_COMPATIBLE_VERSION || version > PM_OBJFILE_VERSION) |
||
377 | Error("Bad version (%d) in model file <%s>",version,filename); |
||
378 | |||
379 | int pof_id; |
||
380 | while (new_pof_read_int(pof_id,model_buf) == 1) |
||
381 | { |
||
382 | pof_id = INTEL_INT(pof_id); |
||
383 | //id = pof_read_int(model_buf); |
||
384 | len = pof_read_int(model_buf); |
||
385 | |||
386 | if (pof_id == ID_GUNS) |
||
387 | { //List of guns on this object |
||
388 | n_guns = pof_read_int(model_buf); |
||
389 | |||
390 | for (int i=0;i<n_guns;i++) { |
||
391 | int sm; |
||
392 | |||
393 | const int gun_id = pof_read_short(model_buf); |
||
394 | sm = pof_read_short(model_buf); |
||
395 | if (sm!=0) |
||
396 | Error("Invalid gun submodel in file <%s>",filename); |
||
397 | pof_read_vecs(&gun_points[gun_id], 1, model_buf); |
||
398 | pof_read_vecs(&gun_dirs[gun_id], 1, model_buf); |
||
399 | } |
||
400 | |||
401 | } |
||
402 | else |
||
403 | pof_cfseek(model_buf,len,SEEK_CUR); |
||
404 | |||
405 | } |
||
406 | r.n_guns = n_guns; |
||
407 | } |
||
408 | |||
409 | //free up a model, getting rid of all its memory |
||
410 | #if defined(DXX_BUILD_DESCENT_I) |
||
411 | static |
||
412 | #endif |
||
413 | void free_model(polymodel &po) |
||
414 | { |
||
415 | po.model_data.reset(); |
||
416 | } |
||
417 | |||
418 | //draw a polygon model |
||
419 | |||
420 | namespace dsx { |
||
421 | |||
422 | void draw_polygon_model(grs_canvas &canvas, const vms_vector &pos, const vms_matrix &orient, const submodel_angles anim_angles, const unsigned model_num, unsigned flags, const g3s_lrgb light, const glow_values_t *const glow_values, alternate_textures alt_textures) |
||
423 | { |
||
424 | Assert(model_num < N_polygon_models); |
||
425 | |||
426 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
427 | const polymodel *po = &Polygon_models[model_num]; |
||
428 | |||
429 | //check if should use simple model |
||
430 | if (po->simpler_model ) //must have a simpler model |
||
431 | if (flags==0) //can't switch if this is debris |
||
432 | //alt textures might not match, but in the one case we're using this |
||
433 | //for on 11/14/94, they do match. So we leave it in. |
||
434 | { |
||
435 | int cnt=1; |
||
436 | const auto depth = g3_calc_point_depth(pos); //gets 3d depth |
||
437 | while (po->simpler_model && depth > cnt++ * Simple_model_threshhold_scale * po->rad) |
||
438 | po = &Polygon_models[po->simpler_model-1]; |
||
439 | } |
||
440 | |||
441 | std::array<grs_bitmap *, MAX_POLYOBJ_TEXTURES> texture_list; |
||
442 | { |
||
443 | const unsigned n_textures = po->n_textures; |
||
444 | std::array<bitmap_index, MAX_POLYOBJ_TEXTURES> texture_list_index; |
||
445 | auto &&tlir = partial_range(texture_list_index, n_textures); |
||
446 | if (alt_textures) |
||
447 | { |
||
448 | for (auto &&[at, tli] : zip(unchecked_partial_range(static_cast<const bitmap_index *>(alt_textures), n_textures), tlir)) |
||
449 | tli = at; |
||
450 | } |
||
451 | else |
||
452 | { |
||
453 | const unsigned first_texture = po->first_texture; |
||
454 | for (auto &&[obp, tli] : zip(partial_range(ObjBitmapPtrs, first_texture, first_texture + n_textures), tlir)) |
||
455 | tli = ObjBitmaps[obp]; |
||
456 | } |
||
457 | |||
458 | // Make sure the textures for this object are paged in... |
||
459 | for (auto &&[tli, tl] : zip(tlir, partial_range(texture_list, n_textures))) |
||
460 | { |
||
461 | tl = &GameBitmaps[tli.index]; |
||
462 | PIGGY_PAGE_IN(tli); |
||
463 | } |
||
464 | } |
||
465 | // Hmmm... cache got flushed in the middle of paging all these in, |
||
466 | // so we need to reread them all in. |
||
467 | // Make sure that they can all fit in memory. |
||
468 | |||
469 | g3_start_instance_matrix(pos, orient); |
||
470 | |||
471 | polygon_model_points robot_points; |
||
472 | |||
473 | if (flags == 0) //draw entire object |
||
474 | |||
475 | g3_draw_polygon_model(&texture_list[0], robot_points, canvas, anim_angles, light, glow_values, po->model_data.get()); |
||
476 | |||
477 | else { |
||
478 | for (int i=0;flags;flags>>=1,i++) |
||
479 | if (flags & 1) { |
||
480 | Assert(i < po->n_models); |
||
481 | |||
482 | //if submodel, rotate around its center point, not pivot point |
||
483 | |||
484 | g3_start_instance_matrix(); |
||
485 | |||
486 | g3_draw_polygon_model(&texture_list[0], robot_points, canvas, anim_angles, light, glow_values, &po->model_data[po->submodel_ptrs[i]]); |
||
487 | |||
488 | g3_done_instance(); |
||
489 | } |
||
490 | } |
||
491 | |||
492 | g3_done_instance(); |
||
493 | } |
||
494 | |||
495 | void free_polygon_models() |
||
496 | { |
||
497 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
498 | range_for (auto &i, partial_range(Polygon_models, N_polygon_models)) |
||
499 | free_model(i); |
||
500 | #if defined(DXX_BUILD_DESCENT_II) |
||
501 | Exit_models_loaded = false; |
||
502 | #endif |
||
503 | } |
||
504 | |||
505 | } |
||
506 | |||
507 | namespace dcx { |
||
508 | |||
509 | static void assign_max(fix &a, const fix &b) |
||
510 | { |
||
511 | a = std::max(a, b); |
||
512 | } |
||
513 | |||
514 | static void assign_min(fix &a, const fix &b) |
||
515 | { |
||
516 | a = std::min(a, b); |
||
517 | } |
||
518 | |||
519 | template <fix vms_vector::*p> |
||
520 | static void update_bounds(vms_vector &minv, vms_vector &maxv, const vms_vector &vp) |
||
521 | { |
||
522 | auto &mx = maxv.*p; |
||
523 | assign_max(mx, vp.*p); |
||
524 | auto &mn = minv.*p; |
||
525 | assign_min(mn, vp.*p); |
||
526 | } |
||
527 | |||
528 | static void assign_minmax(vms_vector &minv, vms_vector &maxv, const vms_vector &v) |
||
529 | { |
||
530 | update_bounds<&vms_vector::x>(minv, maxv, v); |
||
531 | update_bounds<&vms_vector::y>(minv, maxv, v); |
||
532 | update_bounds<&vms_vector::z>(minv, maxv, v); |
||
533 | } |
||
534 | |||
535 | static void polyobj_find_min_max(polymodel *pm) |
||
536 | { |
||
537 | auto &big_mn = pm->mins; |
||
538 | auto &big_mx = pm->maxs; |
||
539 | for (int m=0;m<pm->n_models;m++) { |
||
540 | auto &mn = pm->submodel_mins[m]; |
||
541 | auto &mx = pm->submodel_maxs[m]; |
||
542 | const auto &ofs = pm->submodel_offsets[m]; |
||
543 | |||
544 | auto data = reinterpret_cast<const uint16_t *>(&pm->model_data[pm->submodel_ptrs[m]]); |
||
545 | |||
546 | const auto type = *data++; |
||
547 | |||
548 | Assert(type == 7 || type == 1); |
||
549 | |||
550 | const uint16_t nverts = *data++ - 1; |
||
551 | |||
552 | if (type==7) |
||
553 | data+=2; //skip start & pad |
||
554 | |||
555 | auto vp = reinterpret_cast<const vms_vector *>(data); |
||
556 | |||
557 | mn = mx = *vp++; |
||
558 | |||
559 | if (m==0) |
||
560 | big_mn = big_mx = mn; |
||
561 | |||
562 | range_for (auto &v, unchecked_partial_range(vp, nverts)) |
||
563 | { |
||
564 | assign_minmax(mn, mx, v); |
||
565 | assign_minmax(big_mn, big_mx, vm_vec_add(v, ofs)); |
||
566 | } |
||
567 | } |
||
568 | } |
||
569 | |||
570 | } |
||
571 | |||
572 | namespace dsx { |
||
573 | |||
574 | std::array<char[FILENAME_LEN], MAX_POLYGON_MODELS> Pof_names; |
||
575 | |||
576 | //returns the number of this model |
||
577 | int load_polygon_model(const char *filename,int n_textures,int first_texture,robot_info *r) |
||
578 | { |
||
579 | Assert(N_polygon_models < MAX_POLYGON_MODELS); |
||
580 | Assert(n_textures < MAX_POLYOBJ_TEXTURES); |
||
581 | |||
582 | Assert(strlen(filename) <= 12); |
||
583 | const auto n_models = N_polygon_models; |
||
584 | strcpy(Pof_names[n_models], filename); |
||
585 | |||
586 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
587 | auto &model = Polygon_models[n_models]; |
||
588 | read_model_file(&model, filename, r); |
||
589 | |||
590 | polyobj_find_min_max(&model); |
||
591 | |||
592 | const auto highest_texture_num = g3_init_polygon_model(model.model_data.get(), model.model_data_size); |
||
593 | |||
594 | if (highest_texture_num+1 != n_textures) |
||
595 | Error("Model <%s> references %d textures but specifies %d.",filename,highest_texture_num+1,n_textures); |
||
596 | |||
597 | model.n_textures = n_textures; |
||
598 | model.first_texture = first_texture; |
||
599 | model.simpler_model = 0; |
||
600 | |||
601 | // Assert(polygon_models[N_polygon_models]!=NULL); |
||
602 | |||
603 | N_polygon_models++; |
||
604 | |||
605 | return N_polygon_models-1; |
||
606 | |||
607 | } |
||
608 | |||
609 | } |
||
610 | |||
611 | namespace dcx { |
||
612 | |||
613 | void init_polygon_models() |
||
614 | { |
||
615 | N_polygon_models = 0; |
||
616 | } |
||
617 | |||
618 | } |
||
619 | |||
620 | //compare against this size when figuring how far to place eye for picture |
||
621 | #define BASE_MODEL_SIZE 0x28000 |
||
622 | |||
623 | #define DEFAULT_VIEW_DIST 0x60000 |
||
624 | |||
625 | //draws the given model in the current canvas. The distance is set to |
||
626 | //more-or-less fill the canvas. Note that this routine actually renders |
||
627 | //into an off-screen canvas that it creates, then copies to the current |
||
628 | //canvas. |
||
629 | void draw_model_picture(grs_canvas &canvas, const uint_fast32_t mn, const vms_angvec &orient_angles) |
||
630 | { |
||
631 | g3s_lrgb lrgb = { f1_0, f1_0, f1_0 }; |
||
632 | |||
633 | Assert(mn<N_polygon_models); |
||
634 | |||
635 | gr_clear_canvas(canvas, BM_XRGB(0,0,0)); |
||
636 | g3_start_frame(canvas); |
||
637 | vms_vector temp_pos{}; |
||
638 | g3_set_view_matrix(temp_pos,vmd_identity_matrix,0x9000); |
||
639 | |||
640 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
641 | if (Polygon_models[mn].rad != 0) |
||
642 | temp_pos.z = fixmuldiv(DEFAULT_VIEW_DIST,Polygon_models[mn].rad,BASE_MODEL_SIZE); |
||
643 | else |
||
644 | temp_pos.z = DEFAULT_VIEW_DIST; |
||
645 | |||
646 | const auto &&temp_orient = vm_angles_2_matrix(orient_angles); |
||
647 | draw_polygon_model(canvas, temp_pos, temp_orient, nullptr, mn, 0, lrgb, nullptr, nullptr); |
||
648 | g3_end_frame(); |
||
649 | } |
||
650 | |||
651 | namespace dcx { |
||
652 | |||
653 | DEFINE_SERIAL_VMS_VECTOR_TO_MESSAGE(); |
||
654 | DEFINE_SERIAL_UDT_TO_MESSAGE(polymodel, p, (p.n_models, p.model_data_size, serial::pad<4>(), p.submodel_ptrs, p.submodel_offsets, p.submodel_norms, p.submodel_pnts, p.submodel_rads, p.submodel_parents, p.submodel_mins, p.submodel_maxs, p.mins, p.maxs, p.rad, p.n_textures, p.first_texture, p.simpler_model)); |
||
655 | ASSERT_SERIAL_UDT_MESSAGE_SIZE(polymodel, 12 + (10 * 4) + (10 * 3 * sizeof(vms_vector)) + (10 * sizeof(fix)) + 10 + (10 * 2 * sizeof(vms_vector)) + (2 * sizeof(vms_vector)) + 8); |
||
656 | |||
657 | /* |
||
658 | * reads a polymodel structure from a PHYSFS_File |
||
659 | */ |
||
660 | void polymodel_read(polymodel *pm, PHYSFS_File *fp) |
||
661 | { |
||
662 | pm->model_data.reset(); |
||
663 | PHYSFSX_serialize_read(fp, *pm); |
||
664 | } |
||
665 | |||
666 | } |
||
667 | |||
668 | #if 0 |
||
669 | void polymodel_write(PHYSFS_File *fp, const polymodel &pm) |
||
670 | { |
||
671 | PHYSFSX_serialize_write(fp, pm); |
||
672 | } |
||
673 | #endif |
||
674 | |||
675 | /* |
||
676 | * routine which allocates, reads, and inits a polymodel's model_data |
||
677 | */ |
||
678 | namespace dsx { |
||
679 | void polygon_model_data_read(polymodel *pm, PHYSFS_File *fp) |
||
680 | { |
||
681 | auto model_data_size = pm->model_data_size; |
||
682 | pm->model_data = std::make_unique<uint8_t[]>(model_data_size); |
||
683 | PHYSFS_read(fp, pm->model_data, sizeof(uint8_t), model_data_size); |
||
684 | #if DXX_WORDS_NEED_ALIGNMENT |
||
685 | /* Aligning model data changes pm->model_data_size. Reload it |
||
686 | * afterward. |
||
687 | */ |
||
688 | align_polygon_model_data(pm); |
||
689 | model_data_size = pm->model_data_size; |
||
690 | #endif |
||
691 | if constexpr (words_bigendian) |
||
692 | swap_polygon_model_data(pm->model_data.get()); |
||
693 | #if defined(DXX_BUILD_DESCENT_I) |
||
694 | g3_validate_polygon_model(pm->model_data.get(), model_data_size); |
||
695 | #elif defined(DXX_BUILD_DESCENT_II) |
||
696 | g3_init_polygon_model(pm->model_data.get(), model_data_size); |
||
697 | #endif |
||
698 | } |
||
699 | } |