enable loading of exit models for other than d2demo
[btb/d2x.git] / main / polyobj.c
1 /* $Id: polyobj.c,v 1.8 2002-08-08 09:09:43 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include <conf.h>
17 #endif
18
19 #ifdef RCS
20 static char rcsid[] = "$Id: polyobj.c,v 1.8 2002-08-08 09:09:43 btb Exp $";
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 // -- I hate this warning in make depend! -- #ifdef DRIVE
28 // -- I hate this warning in make depend! -- #include "drive.h"
29 // -- I hate this warning in make depend! -- #else
30 #include "inferno.h"
31 // -- I hate this warning in make depend! -- #endif
32
33 #include "polyobj.h"
34
35 #include "vecmat.h"
36 #include "3d.h"
37 #include "error.h"
38 #include "mono.h"
39 #include "u_mem.h"
40 #include "args.h"
41
42 #ifndef DRIVE
43 #include "texmap.h"
44 #include "bm.h"
45 #include "textures.h"
46 #include "object.h"
47 #include "lighting.h"
48 #include "cfile.h"
49 #include "piggy.h"
50 #endif
51
52 #include "pa_enabl.h"
53
54 #ifdef _3DFX
55 #include "3dfx_des.h"
56 #endif
57
58 polymodel Polygon_models[MAX_POLYGON_MODELS];   // = {&bot11,&bot17,&robot_s2,&robot_b2,&bot11,&bot17,&robot_s2,&robot_b2};
59
60 int N_polygon_models = 0;
61
62 #define MAX_POLYGON_VECS 1000
63 g3s_point robot_points[MAX_POLYGON_VECS];
64
65 #define PM_COMPATIBLE_VERSION 6
66 #define PM_OBJFILE_VERSION 8
67
68 int     Pof_file_end;
69 int     Pof_addr;
70
71 #define MODEL_BUF_SIZE  32768
72
73 void _pof_cfseek(int len,int type)
74 {
75         switch (type) {
76                 case SEEK_SET:  Pof_addr = len; break;
77                 case SEEK_CUR:  Pof_addr += len;        break;
78                 case SEEK_END:
79                         Assert(len <= 0);       //      seeking from end, better be moving back.
80                         Pof_addr = Pof_file_end + len;
81                         break;
82         }
83
84         if (Pof_addr > MODEL_BUF_SIZE)
85                 Int3();
86 }
87
88 #define pof_cfseek(_buf,_len,_type) _pof_cfseek((_len),(_type))
89
90 int pof_read_int(ubyte *bufp)
91 {
92         int i;
93
94         i = *((int *) &bufp[Pof_addr]);
95         Pof_addr += 4;
96         return i;
97
98 //      if (cfread(&i,sizeof(i),1,f) != 1)
99 //              Error("Unexpected end-of-file while reading object");
100 //
101 //      return i;
102 }
103
104 size_t pof_cfread(void *dst, size_t elsize, size_t nelem, ubyte *bufp)
105 {
106         if (Pof_addr + nelem*elsize > Pof_file_end)
107                 return 0;
108
109         memcpy(dst, &bufp[Pof_addr], elsize*nelem);
110
111         Pof_addr += elsize*nelem;
112
113         if (Pof_addr > MODEL_BUF_SIZE)
114                 Int3();
115
116         return nelem;
117 }
118
119 // #define new_read_int(i,f) cfread(&(i),sizeof(i),1,(f))
120 #define new_pof_read_int(i,f) pof_cfread(&(i),sizeof(i),1,(f))
121
122 short pof_read_short(ubyte *bufp)
123 {
124         short s;
125
126         s = *((short *) &bufp[Pof_addr]);
127         Pof_addr += 2;
128         return s;
129 //      if (cfread(&s,sizeof(s),1,f) != 1)
130 //              Error("Unexpected end-of-file while reading object");
131 //
132 //      return s;
133 }
134
135 void pof_read_string(char *buf,int max_char, ubyte *bufp)
136 {
137         int     i;
138
139         for (i=0; i<max_char; i++) {
140                 if ((*buf++ = bufp[Pof_addr++]) == 0)
141                         break;
142         }
143
144 //      while (max_char-- && (*buf=cfgetc(f)) != 0) buf++;
145
146 }
147
148 void pof_read_vecs(vms_vector *vecs,int n,ubyte *bufp)
149 {
150 //      cfread(vecs,sizeof(vms_vector),n,f);
151
152         memcpy(vecs, &bufp[Pof_addr], n*sizeof(*vecs));
153         Pof_addr += n*sizeof(*vecs);
154         
155         if (Pof_addr > MODEL_BUF_SIZE)
156                 Int3();
157 }
158
159 #define ID_OHDR 0x5244484f // 'RDHO'  //Object header
160 #define ID_SOBJ 0x4a424f53 // 'JBOS'  //Subobject header
161 #define ID_GUNS 0x534e5547 // 'SNUG'  //List of guns on this object
162 #define ID_ANIM 0x4d494e41 // 'MINA'  //Animation data
163 #define ID_IDTA 0x41544449 // 'ATDI'  //Interpreter data
164 #define ID_TXTR 0x52545854 // 'RTXT'  //Texture filename list
165
166 #ifdef DRIVE
167 #define robot_info void
168 #else
169 vms_angvec anim_angs[N_ANIM_STATES][MAX_SUBMODELS];
170
171 //set the animation angles for this robot.  Gun fields of robot info must
172 //be filled in.
173 void robot_set_angles(robot_info *r,polymodel *pm,vms_angvec angs[N_ANIM_STATES][MAX_SUBMODELS]);
174 #endif
175
176 #define DEBUG_LEVEL CON_NORMAL
177
178 //reads a binary file containing a 3d model
179 polymodel *read_model_file(polymodel *pm,char *filename,robot_info *r)
180 {
181         CFILE *ifile;
182         short version;
183         int id,len, next_chunk;
184         int anim_flag = 0;
185         ubyte *model_buf;
186
187 #ifdef WORDS_BIGENDIAN
188         Error("read_model_file(): This function is not bigendian-friendly!\n");
189 #endif
190         model_buf = (ubyte *)d_malloc( MODEL_BUF_SIZE * sizeof(ubyte) );
191         if (!model_buf)
192                 Error("Can't allocate space to read model %s\n", filename);
193
194         if ((ifile=cfopen(filename,"rb"))==NULL) 
195                 Error("Can't open file <%s>",filename);
196
197         Assert(ifile->size <= MODEL_BUF_SIZE);
198
199         Pof_addr = 0;
200         Pof_file_end = cfread(model_buf, 1, cfilelength(ifile), ifile);
201         cfclose(ifile);
202
203         id = pof_read_int(model_buf);
204
205         if (id!=0x4f505350) /* 'OPSP' */
206                 Error("Bad ID in model file <%s>",filename);
207
208         version = pof_read_short(model_buf);
209         
210         if (version < PM_COMPATIBLE_VERSION || version > PM_OBJFILE_VERSION)
211                 Error("Bad version (%d) in model file <%s>",version,filename);
212
213         if ( FindArg( "-bspgen" )) 
214                 printf( "bspgen -c1" );
215
216         while (new_pof_read_int(id,model_buf) == 1) {
217
218                 //id  = pof_read_int(model_buf);
219                 len = pof_read_int(model_buf);
220                 next_chunk = Pof_addr + len;
221
222                 switch (id) {
223
224                         case ID_OHDR: {         //Object header
225                                 vms_vector pmmin,pmmax;
226
227                                 //con_printf(DEBUG_LEVEL, "Got chunk OHDR, len=%d\n",len);
228
229                                 pm->n_models = pof_read_int(model_buf);
230                                 pm->rad = pof_read_int(model_buf);
231
232                                 Assert(pm->n_models <= MAX_SUBMODELS);
233
234                                 pof_read_vecs(&pmmin,1,model_buf);
235                                 pof_read_vecs(&pmmax,1,model_buf);
236
237                                 if ( FindArg( "-bspgen" ))      {
238                                         vms_vector v;
239                                         fix l;
240                                 
241                                         vm_vec_sub(&v, &pmmax, &pmmin );
242                                         l = v.x;
243                                         if ( v.y > l ) l = v.y;                                 
244                                         if ( v.z > l ) l = v.z;                                 
245                                                                                                         
246                                         printf( " -l%.3f", f2fl(l) );
247                                 }
248
249                                 break;
250                         }
251                         
252                         case ID_SOBJ: {         //Subobject header
253                                 int n;
254
255                                 anim_flag++;
256
257                                 //con_printf(DEBUG_LEVEL, "Got chunk SOBJ, len=%d\n",len);
258
259                                 n = pof_read_short(model_buf);
260
261                                 Assert(n < MAX_SUBMODELS);
262
263                                 pm->submodel_parents[n] = pof_read_short(model_buf);
264
265                                 pof_read_vecs(&pm->submodel_norms[n],1,model_buf);
266                                 pof_read_vecs(&pm->submodel_pnts[n],1,model_buf);
267                                 pof_read_vecs(&pm->submodel_offsets[n],1,model_buf);
268
269                                 pm->submodel_rads[n] = pof_read_int(model_buf);         //radius
270
271                                 pm->submodel_ptrs[n] = pof_read_int(model_buf); //offset
272
273                                 break;
274
275                         }
276                         
277                         #ifndef DRIVE
278                         case ID_GUNS: {         //List of guns on this object
279
280                                 //con_printf(DEBUG_LEVEL, "Got chunk GUNS, len=%d\n",len);
281
282                                 if (r) {
283                                         int i;
284                                         vms_vector gun_dir;
285                                         ubyte gun_used[MAX_GUNS];
286
287                                         r->n_guns = pof_read_int(model_buf);
288
289                                         if ( r->n_guns )
290                                                 anim_flag++;
291
292                                         Assert(r->n_guns <= MAX_GUNS);
293
294                                         for (i=0;i<r->n_guns;i++)
295                                                 gun_used[i] = 0;
296
297                                         for (i=0;i<r->n_guns;i++) {
298                                                 int id;
299
300                                                 id = pof_read_short(model_buf);
301                                                 Assert(id < r->n_guns);
302                                                 Assert(gun_used[id] == 0);
303                                                 gun_used[id] = 1;
304                                                 r->gun_submodels[id] = pof_read_short(model_buf);
305                                                 Assert(r->gun_submodels[id] != 0xff);
306                                                 pof_read_vecs(&r->gun_points[id],1,model_buf);
307
308                                                 if (version >= 7)
309                                                         pof_read_vecs(&gun_dir,1,model_buf);
310                                         }
311                                 }
312                                 else
313                                         pof_cfseek(model_buf,len,SEEK_CUR);
314
315                                 break;
316                         }
317                         
318                         case ID_ANIM:           //Animation data
319                                 //con_printf(DEBUG_LEVEL, "Got chunk ANIM, len=%d\n",len);
320
321                                 anim_flag++;
322
323                                 if (r) {
324                                         int n_frames,f,m;
325
326                                         n_frames = pof_read_short(model_buf);
327
328                                         Assert(n_frames == N_ANIM_STATES);
329
330                                         for (m=0;m<pm->n_models;m++)
331                                                 for (f=0;f<n_frames;f++)
332                                                         pof_cfread(&anim_angs[f][m],1,sizeof(vms_angvec),model_buf);
333
334                                         robot_set_angles(r,pm,anim_angs);
335                                 
336                                 }
337                                 else
338                                         pof_cfseek(model_buf,len,SEEK_CUR);
339
340                                 break;
341                         #endif
342                         
343                         case ID_TXTR: {         //Texture filename list
344                                 int n;
345                                 char name_buf[128];
346
347                                 //con_printf(DEBUG_LEVEL, "Got chunk TXTR, len=%d\n",len);
348
349                                 n = pof_read_short(model_buf);
350                                 //con_printf(DEBUG_LEVEL, "  num textures = %d\n",n);
351                                 while (n--) {
352                                         pof_read_string(name_buf,128,model_buf);
353                                         //con_printf(DEBUG_LEVEL, "<%s>\n",name_buf);
354                                 }
355
356                                 break;
357                         }
358                         
359                         case ID_IDTA:           //Interpreter data
360                                 //con_printf(DEBUG_LEVEL, "Got chunk IDTA, len=%d\n",len);
361
362                                 pm->model_data = d_malloc(len);
363                                 pm->model_data_size = len;
364
365                                 pof_cfread(pm->model_data,1,len,model_buf);
366
367                                 break;
368
369                         default:
370                                 //con_printf(DEBUG_LEVEL, "Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len);
371                                 pof_cfseek(model_buf,len,SEEK_CUR);
372                                 break;
373
374                 }
375                 if ( version >= 8 )             // Version 8 needs 4-byte alignment!!!
376                         pof_cfseek(model_buf,next_chunk,SEEK_SET);
377         }
378
379 //      for (i=0;i<pm->n_models;i++)
380 //              pm->submodel_ptrs[i] += (int) pm->model_data;
381
382         if ( FindArg( "-bspgen" )) {
383                 char *p = strchr( filename, '.' );
384                 *p = 0;
385
386                 if ( anim_flag > 1 )
387                         printf( " -a" );
388
389                 printf( " %s.3ds\n", filename );
390                 *p = '.';
391         }
392         
393         d_free(model_buf);
394
395         return pm;
396 }
397
398 //reads the gun information for a model
399 //fills in arrays gun_points & gun_dirs, returns the number of guns read
400 int read_model_guns(char *filename,vms_vector *gun_points, vms_vector *gun_dirs, int *gun_submodels)
401 {
402         CFILE *ifile;
403         short version;
404         int id,len;
405         int n_guns=0;
406         ubyte   *model_buf;
407
408         model_buf = (ubyte *)d_malloc( MODEL_BUF_SIZE * sizeof(ubyte) );
409         if (!model_buf)
410                 Error("Can't allocate space to read model %s\n", filename);
411
412         if ((ifile=cfopen(filename,"rb"))==NULL) 
413                 Error("Can't open file <%s>",filename);
414
415         Assert(ifile->size <= MODEL_BUF_SIZE);
416
417         Pof_addr = 0;
418         Pof_file_end = cfread(model_buf, 1, ifile->size, ifile);
419         cfclose(ifile);
420
421         id = pof_read_int(model_buf);
422
423         if (id!=0x4f505350) /* 'OPSP' */
424                 Error("Bad ID in model file <%s>",filename);
425
426         version = pof_read_short(model_buf);
427         
428         Assert(version >= 7);           //must be 7 or higher for this data
429
430         if (version < PM_COMPATIBLE_VERSION || version > PM_OBJFILE_VERSION)
431                 Error("Bad version (%d) in model file <%s>",version,filename);
432
433         while (new_pof_read_int(id,model_buf) == 1) {
434
435                 //id  = pof_read_int(model_buf);
436                 len = pof_read_int(model_buf);
437
438                 if (id == ID_GUNS) {            //List of guns on this object
439
440                         //con_printf(DEBUG_LEVEL, "Got chunk GUNS, len=%d\n",len);
441
442                         int i;
443
444                         n_guns = pof_read_int(model_buf);
445
446                         for (i=0;i<n_guns;i++) {
447                                 int id,sm;
448
449                                 id = pof_read_short(model_buf);
450                                 sm = pof_read_short(model_buf);
451                                 if (gun_submodels)
452                                         gun_submodels[id] = sm;
453                                 else if (sm!=0)
454                                         Error("Invalid gun submodel in file <%s>",filename);
455                                 pof_read_vecs(&gun_points[id],1,model_buf);
456
457                                 pof_read_vecs(&gun_dirs[id],1,model_buf);
458                         }
459
460                 }
461                 else
462                         pof_cfseek(model_buf,len,SEEK_CUR);
463
464         }
465
466         d_free(model_buf);
467         
468         return n_guns;
469 }
470
471 //free up a model, getting rid of all its memory
472 void free_model(polymodel *po)
473 {
474         d_free(po->model_data);
475 }
476
477 grs_bitmap *texture_list[MAX_POLYOBJ_TEXTURES];
478 bitmap_index texture_list_index[MAX_POLYOBJ_TEXTURES];
479
480 int Simple_model_threshhold_scale=5;            //switch when this times radius far away
481
482
483 //draw a polygon model
484
485 void draw_polygon_model(vms_vector *pos,vms_matrix *orient,vms_angvec *anim_angles,int model_num,int flags,fix light,fix *glow_values,bitmap_index alt_textures[])
486 {
487         polymodel *po;
488         int i;
489         PA_DFX (int save_light);
490
491         Assert(model_num < N_polygon_models);
492
493         po=&Polygon_models[model_num];
494
495         //check if should use simple model
496         if (po->simpler_model )                                 //must have a simpler model
497                 if (flags==0)                                                   //can't switch if this is debris
498                         //!!if (!alt_textures) {                                //alternate textures might not match
499                         //alt textures might not match, but in the one case we're using this
500                         //for on 11/14/94, they do match.  So we leave it in.
501                         {
502                                 int cnt=1;
503                                 fix depth;
504         
505                                 depth = g3_calc_point_depth(pos);               //gets 3d depth
506
507                                 while (po->simpler_model && depth > cnt++ * Simple_model_threshhold_scale * po->rad)
508                                         po = &Polygon_models[po->simpler_model-1];
509                         }
510
511         if (alt_textures)
512    {
513                 for (i=0;i<po->n_textures;i++)  {
514                         texture_list_index[i] = alt_textures[i];
515                         texture_list[i] = &GameBitmaps[alt_textures[i].index];
516
517          #ifdef _3DFX
518          texture_list[i]->bm_handle = texture_list_index[i].index;
519          #endif
520                 }
521    }
522         else
523    {
524                 for (i=0;i<po->n_textures;i++)  {
525                         texture_list_index[i] = ObjBitmaps[ObjBitmapPtrs[po->first_texture+i]];
526                         texture_list[i] = &GameBitmaps[ObjBitmaps[ObjBitmapPtrs[po->first_texture+i]].index];
527
528          #ifdef _3DFX
529          texture_list[i]->bm_handle = texture_list_index[i].index;
530          #endif
531                 }
532    }
533
534 #ifdef PIGGY_USE_PAGING
535         // Make sure the textures for this object are paged in...
536         piggy_page_flushed = 0;
537         for (i=0;i<po->n_textures;i++)  
538                 PIGGY_PAGE_IN( texture_list_index[i] );
539         // Hmmm... cache got flushed in the middle of paging all these in,
540         // so we need to reread them all in.
541         if (piggy_page_flushed) {
542                 piggy_page_flushed = 0;
543                 for (i=0;i<po->n_textures;i++)  
544                         PIGGY_PAGE_IN( texture_list_index[i] );
545         }
546         // Make sure that they can all fit in memory.
547         Assert( piggy_page_flushed == 0 );
548 #endif
549
550         g3_start_instance_matrix(pos,orient);
551
552         g3_set_interp_points(robot_points);
553
554 #ifdef _3DFX
555    _3dfx_rendering_poly_obj = 1;
556 #endif
557         PA_DFX(save_light = Lighting_on);
558         PA_DFX(Lighting_on = 0);
559
560         if (flags == 0)         //draw entire object
561
562                 g3_draw_polygon_model(po->model_data,texture_list,anim_angles,light,glow_values);
563
564         else {
565                 int i;
566         
567                 for (i=0;flags;flags>>=1,i++)
568                         if (flags & 1) {
569                                 vms_vector ofs;
570
571                                 Assert(i < po->n_models);
572
573                                 //if submodel, rotate around its center point, not pivot point
574         
575                                 vm_vec_avg(&ofs,&po->submodel_mins[i],&po->submodel_maxs[i]);
576                                 vm_vec_negate(&ofs);
577                                 g3_start_instance_matrix(&ofs,NULL);
578         
579                                 g3_draw_polygon_model(&po->model_data[po->submodel_ptrs[i]],texture_list,anim_angles,light,glow_values);
580         
581                                 g3_done_instance();
582                         }       
583         }
584
585         g3_done_instance();
586
587 #ifdef _3DFX
588    _3dfx_rendering_poly_obj = 0;
589 #endif
590
591         PA_DFX (Lighting_on = save_light);
592
593
594 }
595
596 void free_polygon_models()
597 {
598         int i;
599
600         for (i=0;i<N_polygon_models;i++) {
601                 free_model(&Polygon_models[i]);
602         }
603
604 }
605
606 void polyobj_find_min_max(polymodel *pm)
607 {
608         ushort nverts;
609         vms_vector *vp;
610         ushort *data,type;
611         int m;
612         vms_vector *big_mn,*big_mx;
613         
614         big_mn = &pm->mins;
615         big_mx = &pm->maxs;
616
617         for (m=0;m<pm->n_models;m++) {
618                 vms_vector *mn,*mx,*ofs;
619
620                 mn = &pm->submodel_mins[m];
621                 mx = &pm->submodel_maxs[m];
622                 ofs= &pm->submodel_offsets[m];
623
624                 data = (ushort *)&pm->model_data[pm->submodel_ptrs[m]];
625         
626                 type = *data++;
627         
628                 Assert(type == 7 || type == 1);
629         
630                 nverts = *data++;
631         
632                 if (type==7)
633                         data+=2;                //skip start & pad
634         
635                 vp = (vms_vector *) data;
636         
637                 *mn = *mx = *vp++; nverts--;
638
639                 if (m==0)
640                         *big_mn = *big_mx = *mn;
641         
642                 while (nverts--) {
643                         if (vp->x > mx->x) mx->x = vp->x;
644                         if (vp->y > mx->y) mx->y = vp->y;
645                         if (vp->z > mx->z) mx->z = vp->z;
646         
647                         if (vp->x < mn->x) mn->x = vp->x;
648                         if (vp->y < mn->y) mn->y = vp->y;
649                         if (vp->z < mn->z) mn->z = vp->z;
650         
651                         if (vp->x+ofs->x > big_mx->x) big_mx->x = vp->x+ofs->x;
652                         if (vp->y+ofs->y > big_mx->y) big_mx->y = vp->y+ofs->y;
653                         if (vp->z+ofs->z > big_mx->z) big_mx->z = vp->z+ofs->z;
654         
655                         if (vp->x+ofs->x < big_mn->x) big_mn->x = vp->x+ofs->x;
656                         if (vp->y+ofs->y < big_mn->y) big_mn->y = vp->y+ofs->y;
657                         if (vp->z+ofs->z < big_mn->z) big_mn->z = vp->z+ofs->z;
658         
659                         vp++;
660                 }
661
662 //              printf("Submodel %d:  (%8x,%8x) (%8x,%8x) (%8x,%8x)\n",m,mn->x,mx->x,mn->y,mx->y,mn->z,mx->z);
663         }
664
665 //      printf("Whole model: (%8x,%8x) (%8x,%8x) (%8x,%8x)\n",big_mn->x,big_mx->x,big_mn->y,big_mx->y,big_mn->z,big_mx->z);
666
667 }
668
669 extern short highest_texture_num;       //from the 3d
670
671 char Pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
672
673 //returns the number of this model
674 #ifndef DRIVE
675 int load_polygon_model(char *filename,int n_textures,int first_texture,robot_info *r)
676 #else
677 int load_polygon_model(char *filename,int n_textures,grs_bitmap ***textures)
678 #endif
679 {
680         #ifdef DRIVE
681         #define r NULL
682         #endif
683
684         Assert(N_polygon_models < MAX_POLYGON_MODELS);
685         Assert(n_textures < MAX_POLYOBJ_TEXTURES);
686
687         //      MK was real tired of those useless, slow mprintfs...
688         if (N_polygon_models > MAX_POLYGON_MODELS - 10)
689                 mprintf(( 0, "Used %d/%d polygon model slots\n", N_polygon_models+1, MAX_POLYGON_MODELS ));
690
691         Assert(strlen(filename) <= 12);
692         strcpy(Pof_names[N_polygon_models],filename);
693
694         read_model_file(&Polygon_models[N_polygon_models],filename,r);
695
696         polyobj_find_min_max(&Polygon_models[N_polygon_models]);
697
698         g3_init_polygon_model(Polygon_models[N_polygon_models].model_data);
699
700         if (highest_texture_num+1 != n_textures)
701                 Error("Model <%s> references %d textures but specifies %d.",filename,highest_texture_num+1,n_textures);
702
703         Polygon_models[N_polygon_models].n_textures = n_textures;
704         Polygon_models[N_polygon_models].first_texture = first_texture;
705         Polygon_models[N_polygon_models].simpler_model = 0;
706
707 //      Assert(polygon_models[N_polygon_models]!=NULL);
708
709         N_polygon_models++;
710
711         return N_polygon_models-1;
712
713 }
714
715
716 void init_polygon_models()
717 {
718         N_polygon_models = 0;
719
720         atexit((void (*)())free_polygon_models);
721
722 }
723
724 //compare against this size when figuring how far to place eye for picture
725 #define BASE_MODEL_SIZE 0x28000
726
727 #define DEFAULT_VIEW_DIST 0x60000
728
729 //draws the given model in the current canvas.  The distance is set to
730 //more-or-less fill the canvas.  Note that this routine actually renders
731 //into an off-screen canvas that it creates, then copies to the current
732 //canvas.
733 void draw_model_picture(int mn,vms_angvec *orient_angles)
734 {
735         vms_vector      temp_pos=ZERO_VECTOR;
736         vms_matrix      temp_orient = IDENTITY_MATRIX;
737         grs_canvas      *save_canv = grd_curcanv,*temp_canv;
738
739         Assert(mn>=0 && mn<N_polygon_models);
740
741         temp_canv = gr_create_canvas(save_canv->cv_bitmap.bm_w,save_canv->cv_bitmap.bm_h);
742         gr_set_current_canvas(temp_canv);
743         gr_clear_canvas( BM_XRGB(0,0,0) );
744
745         g3_start_frame();
746         g3_set_view_matrix(&temp_pos,&temp_orient,0x9000);
747
748         if (Polygon_models[mn].rad != 0)
749                 temp_pos.z = fixmuldiv(DEFAULT_VIEW_DIST,Polygon_models[mn].rad,BASE_MODEL_SIZE);
750         else
751                 temp_pos.z = DEFAULT_VIEW_DIST;
752
753         vm_angles_2_matrix(&temp_orient, orient_angles);
754
755         PA_DFX(save_light = Lighting_on);
756         PA_DFX(Lighting_on = 0);
757         draw_polygon_model(&temp_pos,&temp_orient,NULL,mn,0,f1_0,NULL,NULL);
758         PA_DFX (Lighting_on = save_light);
759
760         gr_set_current_canvas(save_canv);
761
762         gr_bitmap(0,0,&temp_canv->cv_bitmap);
763
764         gr_free_canvas(temp_canv);
765 }
766
767 #ifndef FAST_FILE_IO
768 /*
769  * reads a polymodel structure from a CFILE
770  */
771 extern void polymodel_read(polymodel *pm, CFILE *fp)
772 {
773         int i;
774
775         pm->n_models = cfile_read_int(fp);
776         pm->model_data_size = cfile_read_int(fp);
777         pm->model_data = (ubyte *) cfile_read_int(fp);
778         for (i = 0; i < MAX_SUBMODELS; i++)
779                 pm->submodel_ptrs[i] = cfile_read_int(fp);
780         for (i = 0; i < MAX_SUBMODELS; i++)
781                 cfile_read_vector(&(pm->submodel_offsets[i]), fp);
782         for (i = 0; i < MAX_SUBMODELS; i++)
783                 cfile_read_vector(&(pm->submodel_norms[i]), fp);
784         for (i = 0; i < MAX_SUBMODELS; i++)
785                 cfile_read_vector(&(pm->submodel_pnts[i]), fp);
786         for (i = 0; i < MAX_SUBMODELS; i++)
787                 pm->submodel_rads[i] = cfile_read_fix(fp);
788         cfread(pm->submodel_parents, MAX_SUBMODELS, 1, fp);
789         for (i = 0; i < MAX_SUBMODELS; i++)
790                 cfile_read_vector(&(pm->submodel_mins[i]), fp);
791         for (i = 0; i < MAX_SUBMODELS; i++)
792                 cfile_read_vector(&(pm->submodel_maxs[i]), fp);
793         cfile_read_vector(&(pm->mins), fp);
794         cfile_read_vector(&(pm->maxs), fp);
795         pm->rad = cfile_read_fix(fp);
796         pm->n_textures = cfile_read_byte(fp);
797         pm->first_texture = cfile_read_short(fp);
798         pm->simpler_model = cfile_read_byte(fp);
799 }
800
801 /*
802  * reads n polymodel structs from a CFILE
803  */
804 extern int polymodel_read_n(polymodel *pm, int n, CFILE *fp)
805 {
806         int i, j;
807
808         for (i = 0; i < n; i++) {
809                 pm[i].n_models = cfile_read_int(fp);
810                 pm[i].model_data_size = cfile_read_int(fp);
811                 pm[i].model_data = (ubyte *) cfile_read_int(fp);
812                 for (j = 0; j < MAX_SUBMODELS; j++)
813                         pm[i].submodel_ptrs[j] = cfile_read_int(fp);
814                 for (j = 0; j < MAX_SUBMODELS; j++)
815                         cfile_read_vector(&(pm[i].submodel_offsets[j]), fp);
816                 for (j = 0; j < MAX_SUBMODELS; j++)
817                         cfile_read_vector(&(pm[i].submodel_norms[j]), fp);
818                 for (j = 0; j < MAX_SUBMODELS; j++)
819                         cfile_read_vector(&(pm[i].submodel_pnts[j]), fp);
820                 for (j = 0; j < MAX_SUBMODELS; j++)
821                         pm[i].submodel_rads[j] = cfile_read_fix(fp);
822                 cfread(pm[i].submodel_parents, MAX_SUBMODELS, 1, fp);
823                 for (j = 0; j < MAX_SUBMODELS; j++)
824                         cfile_read_vector(&(pm[i].submodel_mins[j]), fp);
825                 for (j = 0; j < MAX_SUBMODELS; j++)
826                         cfile_read_vector(&(pm[i].submodel_maxs[j]), fp);
827                 cfile_read_vector(&(pm[i].mins), fp);
828                 cfile_read_vector(&(pm[i].maxs), fp);
829                 pm[i].rad = cfile_read_fix(fp);
830                 pm[i].n_textures = cfile_read_byte(fp);
831                 pm[i].first_texture = cfile_read_short(fp);
832                 pm[i].simpler_model = cfile_read_byte(fp);
833         }
834         return i;
835 }
836 #endif