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