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 | } |