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