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