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