source: golgotha/src/render/tmanage.cc @ 80

Last change on this file since 80 was 80, checked in by Sam Hocevar, 11 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 21.2 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9#include "tmanage.hh"
10#include "memory/malloc.hh"
11#include "error/error.hh"
12#include "checksum/checksum.hh"
13#include "image/color.hh"
14#include "string/str_checksum.hh"
15#include "error/alert.hh"
16#include "time/profile.hh"
17#include "string/string.hh"
18#include "status/status.hh"
19#include "file/ram_file.hh"
20#include "tex_cache.hh"
21#include "file/file.hh"
22#include "search.hh"
23#include "r1_res.hh"
24#include "image/image.hh"
25#include "image/context.hh"
26#include "time/profile.hh"
27
28i4_profile_class pf_register_texture("register_texture");
29
30w32 R1_CHROMA_COLOR = (254<<16) | (2<<8) | (166);
31
32i4_profile_class pf_get_texture("tmanage get texture");
33
34r1_texture_manager_class *r1_texture_manager=0;
35
36r1_texture_manager_class::r1_texture_manager_class(const i4_pal *pal)
37  : pal(pal),
38    registered_tnames(0,128)
39{
40  textures_loaded=i4_F;
41  square_textures = i4_F;
42}
43
44void r1_texture_manager_class::init()
45{
46  texture_load_toggle = i4_T;
47  texture_resolution_changed = i4_F;
48  textures_loaded=i4_F;
49 
50  frame_count       = 0;
51  entries           = 0;
52  tanims            = 0;
53
54  total_tanims   = 0;
55  total_textures = 0;
56
57  registered_tnames.clear();
58}
59
60char *r1_texture_manager_class::get_texture_name(r1_texture_handle handle)
61{
62  if (handle<registered_tnames.size())
63    return registered_tnames[handle].name;
64  else
65    return "invalid";
66}
67
68
69void r1_texture_manager_class::uninit()
70{
71  sw32 i,j;
72
73  for (i=1; i<total_textures; i++)
74  {   
75    for (j=0; j<R1_MAX_MIP_LEVELS; j++)
76    {
77      r1_miplevel_t *m = (*entries)[i].mipmaps[j];
78      if (m)
79      {
80        if(m->vram_handle)
81        {
82          free_mip(m->vram_handle);
83          m->vram_handle = 0;
84        }
85        delete m;
86        m = 0;
87      }
88    }
89//    delete (*entries)[i];
90  }
91 
92  if (entries)
93  {
94    delete entries;
95    entries = 0;
96  }
97 
98  registered_tnames.uninit();
99
100  texture_load_toggle = i4_T;
101  texture_resolution_changed=i4_F;
102
103  frame_count = 0;
104  tanims      = 0;
105
106  total_tanims   = 0;
107  total_textures = 0;
108  textures_loaded = i4_F;
109}
110
111void r1_texture_manager_class::reset()
112{
113  uninit();
114  init();
115}
116
117int entry_compare(const r1_texture_entry_struct *a, const r1_texture_entry_struct *b)
118{
119  if (a->id<b->id)
120    return -1;
121  else if (a->id>b->id)
122    return 1;
123  else return 0;
124}
125
126r1_texture_handle r1_texture_manager_class::find_texture(w32 id)
127{
128  sw32 lo=1,hi=total_textures-1,mid;
129
130  if (!entries)
131    return 0;
132
133  r1_texture_entry_struct search_entry;
134  search_entry.id = id;
135
136  sw32 location = entries->binary_search(&search_entry,entry_compare);
137 
138  if (location==-1)
139    return 0;
140 
141  return location;
142}
143
144
145void r1_texture_manager_class::matchup_textures()
146{
147  sw32 i;
148  for (i=0; i<registered_tnames.size(); i++)
149  {
150    r1_texture_handle h = find_texture(registered_tnames[i].id);
151   
152    if (h)
153      registered_tnames[i].handle = h;
154    else
155    {
156      registered_tnames[i].handle = 0;
157      i4_warning("Texture matchup failed: %s",registered_tnames[i].name);
158    }
159  }
160
161 
162}
163
164int name_compare(const r1_texture_matchup_struct *a, const r1_texture_matchup_struct *b)
165{
166  return strcmp(a->name, b->name);
167}
168
169
170r1_texture_handle r1_texture_manager_class::register_texture(const i4_const_str &tname,
171                                                             const i4_const_str &error_string,
172                                                             i4_bool *has_been_loaded)
173{
174  pf_register_texture.start();
175
176  if (textures_loaded)
177    i4_error("textures already loaded");
178
179  int found = -1;
180
181
182  r1_texture_matchup_struct t;
183  i4_os_string(tname, t.name, 128);
184  t.id     = r1_get_texture_id(tname);
185  t.handle = 0;
186  t.left   = -1;
187  t.right  = -1;
188
189  if (has_been_loaded)
190    *has_been_loaded=0;
191
192  // search the tree for the name
193  if (registered_tnames.size())
194  {
195    int root=0, parent=-1;
196    while (found<0)
197    {
198      parent=root;
199
200      int res=strcmp(registered_tnames[root].name, t.name);
201      if (res==0)
202      {
203        if (has_been_loaded)
204          *has_been_loaded=1;
205        found = root;
206      }
207      else if (res<0)
208        root=registered_tnames[root].left;
209      else
210        root=registered_tnames[root].right;
211
212      if (root==-1)
213      {
214        if (res<0)
215          found = registered_tnames[parent].left=registered_tnames.add(t);
216        else
217          found = registered_tnames[parent].right=registered_tnames.add(t);
218      }
219    }
220  }
221  else
222    found=registered_tnames.add(t);
223
224  pf_register_texture.stop();
225  return found;
226}
227
228int w32_compare(const w32 *a, const w32 *b)
229{
230  if (*a < *b)
231    return -1;
232  else
233  if (*a > *b)
234    return 1;
235  else
236    return 0; 
237}
238
239w32 r1_get_file_id(const i4_const_str &fname)
240{
241  int x;
242  char st[30], *s;
243  s=st;
244 
245  i4_const_str::iterator l=fname.begin();
246  while (l!=fname.end() && l.get().ascii_value()!='.')
247  {
248    *(s++)=l.get().ascii_value();
249    ++l;
250  }
251
252  *s=0;
253
254  if (sscanf(st,"%x",&x))
255    return x;
256  else return 0;   
257}
258
259void r1_texture_manager_class::toggle_texture_loading()
260{
261  texture_load_toggle = (i4_bool)(!texture_load_toggle);
262}
263
264r1_miplevel_t *r1_texture_manager_class::get_texture(r1_texture_handle _handle,
265                                                     w32 frame_counter,
266                                                     sw32 desired_width,
267                                                     sw32 &ret_w, sw32 &ret_h)
268{
269  pf_get_texture.start(); 
270
271  if (desired_width > max_texture_dimention)
272    desired_width = max_texture_dimention;
273
274  r1_texture_entry_struct *e;
275 
276  r1_texture_handle handle = registered_tnames[_handle].handle;
277  if (handle==0)
278    i4_error("get_texture called with invalid handle");
279
280  if (handle>=total_textures)
281    i4_error("asking for bad texture");
282
283  if (handle<0)  // this is an animation
284  {
285    r1_texture_animation_entry_struct *ta=tanims+(-handle-1);
286    handle=ta->frames[frame_counter%ta->total_frames];
287    if (!handle)
288    {
289      pf_get_texture.stop();
290      return 0;
291    }
292  }
293 
294  e = &(*entries)[handle];
295 
296  sw32 i = 0; 
297  sw32 highest_resident = -1;
298  sw32 need_to_use = 0;
299 
300  r1_miplevel_t *t = 0;
301
302  //find the highest resident mip. can be precalculated and updated ?
303  for (i=0; i<R1_MAX_MIP_LEVELS; i++)
304  {
305    t = e->mipmaps[i];
306   
307    if (t)
308    {
309      if (t->width >= desired_width)
310        need_to_use = i;
311
312      if (t->vram_handle)   
313      {       
314        if (t->width < desired_width)
315        {
316          //if there were no more higher than this,
317          //this is the best we can do
318          if (highest_resident==-1)
319            highest_resident = i;
320          break;
321        }
322        else
323          highest_resident = i;
324      }           
325    }
326    else
327    {
328      break;
329    } 
330  } 
331 
332  if (highest_resident==-1)
333  {
334    i4_warning("No textures resident for handle %d", handle);
335    return 0;   
336  } 
337 
338  r1_miplevel_t *return_mip = e->mipmaps[highest_resident];
339 
340  if (!return_mip)
341    i4_error("catastophic error - r1_texture_manager_class::return_mip is 0");
342
343  if (!return_mip->vram_handle)
344    i4_warning("returned a mip w/no vram handle");     
345
346  //this is to prevent the load below from deleting this miplevel from vram
347  sw32 old_last_frame = return_mip->last_frame_used;
348  return_mip->last_frame_used = frame_count;
349 
350  //loading information
351  r1_mip_load_info load_info;
352
353  //i4_warning("texture loading is OFF");
354  if (texture_load_toggle && (highest_resident != need_to_use) && e->mipmaps[need_to_use])
355  {
356    load_info.src_file = 0;
357    load_info.dest_mip = e->mipmaps[need_to_use];     
358    //should be asynchronous
359     
360    //prevents from trying to load this same mip level twice at the same time
361    if (!(load_info.dest_mip->flags & R1_MIPLEVEL_IS_LOADING))
362    {
363      //if there is no room for this mip, load up as high a level as possible
364      while (!async_mip_load(&load_info))
365      {
366        if (load_info.error==R1_MIP_LOAD_NO_ROOM)
367        {
368          //try to load a lower res
369          need_to_use++;
370          if (need_to_use==highest_resident) break;
371          if (!e->mipmaps[need_to_use]) break;
372          if (e->mipmaps[need_to_use]->flags & R1_MIPLEVEL_IS_LOADING) break;
373          load_info.dest_mip = e->mipmaps[need_to_use];
374        }
375        else
376          break;
377      }
378    }
379    //else
380    //{
381    //  i4_warning("texture still loading, wont queue");
382    //}
383  } 
384     
385  pf_get_texture.stop();
386 
387  //store the old information back.
388  return_mip->last_frame_used = old_last_frame;
389
390  return return_mip;
391}
392
393w32 r1_texture_manager_class::average_texture_color(r1_texture_handle _handle, w32 frame_num)
394{
395  r1_texture_handle handle = registered_tnames[_handle].handle;
396
397  r1_texture_entry_struct *e;
398 
399  if (handle>=total_textures)
400    i4_error("asking for bad texture");
401
402  if (handle<0)  // this is an animation
403  {
404    r1_texture_animation_entry_struct *ta=tanims+(-handle-1);
405    handle=ta->frames[frame_num%ta->total_frames];
406    if (!handle)
407    {
408      pf_get_texture.stop();
409      return 0;
410    }
411  }
412 
413  e = &(*entries)[handle];
414
415  return e->average_color;
416}
417
418
419void r1_texture_manager_class::next_frame()
420{
421  frame_count++;
422  //eventually (after a long fucking time) this could
423  //wrap around. just make sure its always > 0
424  if (frame_count<0) frame_count=0;
425}
426
427int tex_entry_compare(const tex_cache_entry_t *a, const tex_cache_entry_t *b)
428{
429  if (a->id > b->id)
430    return 1;
431  else
432  if (a->id < b->id)
433    return -1;
434  else
435    return 0;
436}
437
438tex_cache_entry_t *find_id_in_tex_cache(tex_cache_entry_t *entries, w32 num_entries, w32 search_id)
439{
440  if (!entries || num_entries==0)
441    return 0;
442
443  w32 res;
444 
445  tex_cache_entry_t search;
446  search.id = search_id;
447
448  if (!i4_base_bsearch(&search,res,entries,
449                       sizeof(tex_cache_entry_t),
450                       num_entries,
451                       (i4_bsearch_compare_function_type)tex_entry_compare))
452    return 0;
453
454  return entries+res;
455}
456
457i4_bool palettes_are_same(i4_pixel_format *a, i4_pixel_format *b)
458{
459  sw32 i;
460 
461  w8 *compare_a = (w8 *)a;
462  w8 *compare_b = (w8 *)b;
463
464  for (i=0;i<sizeof(i4_pixel_format); i++)
465  {
466    if (*compare_a != *compare_b)
467      return i4_F;
468
469    compare_a++;
470    compare_b++;
471  }
472 
473  return i4_T;
474}
475
476#include "tex_cache.cc"
477#include "tex_heap.cc"
478
479void r1_texture_manager_class::keep_resident(const i4_const_str &tname, sw32 desired_width)
480{
481  w32 id = r1_get_texture_id(tname);
482  sw16 handle = find_texture(id);
483  if (!handle) return;
484   
485  r1_texture_entry_struct *t = &(*entries)[handle];
486
487  r1_mip_load_info load_info;
488
489  //load all levels <= desired_width
490  load_info.src_file = 0;
491 
492  sw32 j;
493  for (j=0; j<R1_MAX_MIP_LEVELS; j++)
494  {         
495    r1_miplevel_t *mip = t->mipmaps[j];
496   
497    if (mip && (mip->width <= desired_width))
498    {     
499      mip->last_frame_used = -1;
500
501      if ((mip->vram_handle==0) && (!(mip->flags & R1_MIPLEVEL_IS_LOADING)))
502      {
503        load_info.dest_mip = mip;     
504        if (!immediate_mip_load(&load_info))
505        {
506          //these absolutely must work
507          i4_error("keep_resident::could not load texture, cannot continue");       
508        }
509      }
510    }
511  }
512}
513
514i4_bool r1_texture_manager_class::load_textures()
515
516  sw32 i,j;
517
518  if (textures_loaded)
519    return i4_F;
520
521   r1_texture_ref *p=r1_texture_ref::first;
522  for (; p; p=p->next)
523    p->texture_handle=register_texture(p->name, "code reference");
524 
525  //create an array of texture id's from names[] and sort it
526  i4_array<w32> texture_file_ids(128,128); 
527
528  for (i=0; i<registered_tnames.size(); i++)
529    texture_file_ids.add(registered_tnames[i].id);
530
531
532  //sort the list
533  texture_file_ids.sort(w32_compare);
534
535  //update / build / rebuild the cache file
536  keep_cache_current(&texture_file_ids);
537
538  i4_file_class *cache_file = i4_open(r1_get_cache_file(), I4_READ | I4_NO_BUFFER); 
539
540  tex_cache_header_t tex_cache_header;
541
542  if (cache_file)
543  {     
544    //read in the header for further processing
545    tex_cache_header.read(cache_file);   
546  }
547  else
548  {
549    i4_warning("Couldnt locate texture cache file.");
550    tex_cache_header.num_entries = 0;
551    tex_cache_header.entries     = 0;
552  }
553 
554
555  //OK FINALLY. process this crap. load information for all
556  //requested textures (their ids are currently in texture_file_ids)
557 
558  //dynamic array of loaded texture entries   
559  i4_array<r1_texture_entry_struct> new_texture_entries(128,128);
560
561  i4_status_class *stat = i4_create_status(r1_gets("loading_textures")); 
562
563  for (i=0; i<texture_file_ids.size(); i++)
564  {
565    w32 id = texture_file_ids[i];
566   
567    tex_cache_entry_t *t = find_id_in_tex_cache(tex_cache_header.entries,
568                                                tex_cache_header.num_entries,id);
569
570    if (!t || (t->lowmipoffset==0xFFFFFFFF))
571    {
572      for (int k=0; k<registered_tnames.size(); k++)
573        if (registered_tnames[k].id==id)
574        {
575          i4_warning("Texture: %s not found in texture cache, run maxtool 'Update textures'", registered_tnames[k].name);
576        }
577    }
578    else   
579    {
580      //texture is in the cache. need to load up some info, load the lowest mip, etc
581      //add it to the list of valid textures
582      r1_texture_entry_struct *new_entry = new_texture_entries.add();
583
584      //the last one should always be null, hence the R1_MAX_MIP_LEVELS+1
585      memset(new_entry->mipmaps,0,sizeof(r1_miplevel_t *) * (R1_MAX_MIP_LEVELS+1));
586     
587      new_entry->flags         = t->flags;
588      new_entry->id            = t->id;
589      new_entry->average_color = t->average_color;                           
590     
591      generate_mip_offsets(t->base_width,t->base_height,t->num_mip_levels,(sw32 *)new_entry->file_offsets,2);
592
593      //fill in this structure. information on mip levels
594      for (j=0; j<t->num_mip_levels; j++)
595      {
596        new_entry->mipmaps[j] = new r1_miplevel_t;
597     
598        r1_miplevel_t *mip = new_entry->mipmaps[j];
599
600        mip->level  = j;
601        mip->width  = t->base_width /(1<<j);
602        mip->height = t->base_height/(1<<j);
603        mip->entry  = new_entry;
604               
605        mip->flags = 0;
606      }
607
608      //seek to the low mip offset (stored IN the cache file)
609      cache_file->seek(t->lowmipoffset+8);
610
611      r1_mip_load_info load_info;
612   
613      //the dst_mip is the very last one   
614      load_info.src_file = cache_file;
615      load_info.dest_mip = new_entry->mipmaps[t->num_mip_levels-1]; 
616       
617      //dont want these to ever be thrown out of texture memory
618      load_info.dest_mip->last_frame_used   = -1;     
619
620      //load that low mip level
621      if (!immediate_mip_load(&load_info))
622      {
623        //check the error field in load_info
624        i4_error("Could not load lowest miplevel of a texture, cannot continue");
625      }
626    }
627
628    if (stat)
629      stat->update((float)(i+1) / (float)texture_file_ids.size());
630  }
631       
632  if (stat)
633    delete stat;
634 
635  if (cache_file)
636    delete cache_file;
637
638  if (tex_cache_header.entries)
639    i4_free(tex_cache_header.entries);
640  else
641  {
642    //there were no texture cache entries? just get rid of the file.
643    i4_unlink(r1_get_cache_file());
644  }
645 
646
647  //theres 1 additional texture, which will be entries[0], the default texture
648  total_textures = new_texture_entries.size() + 1;
649
650  entries = new i4_array<r1_texture_entry_struct>(total_textures,8);
651 
652  r1_texture_entry_struct blank_entry;
653  memset(&blank_entry,0,sizeof(r1_texture_entry_struct));
654  blank_entry.average_color = 0x00FFFFFF;
655
656  entries->add(blank_entry);
657
658  //should be sorted already but go ahead, sort again just in case
659  if (new_texture_entries.size())
660    new_texture_entries.sort(entry_compare); 
661
662  for (i=0; i<total_textures-1; i++)
663  {   
664    entries->add();
665    memset(&(*entries)[i+1],0,sizeof(r1_texture_entry_struct));
666   
667    (*entries)[i+1] = new_texture_entries[i];
668   
669    //crap. have to update the entry pointers since the mip's ->entry
670    //references are in new_texture_entries (instead of entries[])
671    for (j=0; j<R1_MAX_MIP_LEVELS; j++)
672    {
673      if ((*entries)[i+1].mipmaps[j])
674      {
675        (*entries)[i+1].mipmaps[j]->entry = &(*entries)[i+1];
676      }
677    }
678  }   
679
680  matchup_textures();
681
682  /*
683  i4_array<r1_texture_animation_entry_struct> anim_a(128,128);
684  for (i=0; i<names.size(); i++)
685  {
686    w32 id=r1_get_texture_id(*names[i]);
687    i4_str *fn=r1_animation_id_to_filename(id);
688    i4_file_class *fp=opener(*fn);
689    if (fp)
690    {
691      r1_texture_animation_entry_struct *a=anim_a.add();
692      a->id=fp->read_32();
693      a->total_frames=fp->read_16();
694      a->frames=(r1_texture_handle *)i4_malloc(sizeof(r1_texture_handle)*a->total_frames,
695                                                "animation frames");   
696      for (j=0; j<a->total_frames; j++)
697      {
698        w32 id=fp->read_32();
699        a->frames[j]=find_texture(id);
700      }
701    }
702
703    delete fn;
704  } 
705
706  total_tanims=anim_a.size();
707  if (total_tanims)
708  {
709    tanims=(r1_texture_animation_entry_struct *)i4_malloc(sizeof(r1_texture_animation_entry_struct)
710                                                          * total_tanims, "animations"); 
711    for (i=0; i<anim_a.size(); i++)
712      tanims[i]=anim_a[i];
713  }
714  else
715    tanims=0;
716
717  */
718  return i4_T;
719}
720
721r1_texture_handle r1_texture_manager_class::register_image(i4_image_class *image)
722{
723  if (!entries || !entries->size())
724    load_textures();
725
726  //check to make sure image is power of 2
727  sw32 i,j,new_width,new_height;
728
729  for (i=1; i < image->width();  i = i<<1);
730  new_width  = i;
731
732  for (i=1; i < image->height(); i = i<<1);
733  new_height = i;
734
735 
736  r1_texture_handle return_handle = 0;
737
738  i4_draw_context_class context(0,0,image->width()-1,image->height()-1);
739
740
741  r1_texture_entry_struct new_entry;
742  memset(&new_entry,0,sizeof(r1_texture_entry_struct));
743
744
745  const i4_pal *put_pal=pal;
746  if (image->pal->source.alpha_bits)
747  {
748    put_pal=i4_pal_man.register_pal(&alpha_format);
749    new_entry.flags|=R1_MIP_IS_ALPHATEXTURE;
750  }
751
752
753  i4_image_class *temp_image    = i4_create_image(image->width(),image->height(), put_pal);
754  i4_image_class *texture_image = temp_image;
755 
756  image->put_image(temp_image,0,0,context);
757 
758  if (new_width != temp_image->width() || new_height != temp_image->height())
759  {
760    texture_image = i4_create_image(new_width,new_height, pal);
761   
762    sw32 old_width  = temp_image->width();
763    sw32 old_height = temp_image->height();
764
765    w16 *old_tex = (w16 *)temp_image->data;
766    w16 *new_tex = (w16 *)texture_image->data;
767    w16 *dst     = new_tex;
768 
769    float width_ratio  = (float)old_width  / (float)new_width;
770    float height_ratio = (float)old_height / (float)new_height;
771   
772    //now scale the old to fit the new   
773
774    for (j=0; j<new_height; j++)
775    for (i=0; i<new_width;  i++, dst++)
776    {   
777      *dst = old_tex[(sw32)((double)j * height_ratio)*old_width + (sw32)((double)i * width_ratio)];
778    }
779   
780    delete temp_image;
781  }   
782
783  i4_ram_file_class *fake_file = new i4_ram_file_class(texture_image->data,
784                                                       texture_image->width() *
785                                                       texture_image->height() * 2);
786
787
788  new_entry.id            = (*entries)[entries->size()-1].id + 1; //maintains the sorted order of the array
789  new_entry.average_color = 0xFFFFFFFF;
790  new_entry.mipmaps[0]    = new r1_miplevel_t;
791 
792  r1_miplevel_t *mip = new_entry.mipmaps[0];
793
794  mip->level  = 0;
795  mip->width  = texture_image->width();
796  mip->height = texture_image->height();
797 
798  mip->last_frame_used = -1;
799  mip->vram_handle     = 0;
800  mip->flags = R1_MIP_IS_ALPHATEXTURE;
801
802  entries->add(new_entry);
803  total_textures++;
804
805  //update entry pointers for all miplevels 
806  for (i=0; i<total_textures-1; i++)
807  {
808    //references are in new_texture_entries (instead of entries[])
809    for (j=0; j<R1_MAX_MIP_LEVELS; j++)
810    {
811      if ((*entries)[i+1].mipmaps[j])
812      {
813        (*entries)[i+1].mipmaps[j]->entry = &(*entries)[i+1];
814      }
815    }
816  }
817   
818  r1_mip_load_info load_info;
819  load_info.dest_mip = mip;
820  load_info.src_file = fake_file;
821
822  if (!immediate_mip_load(&load_info))
823  {
824    i4_warning("tmanager:: register_image failed.");
825  }
826  else
827  {
828    return_handle = total_textures-1; 
829  }
830 
831  delete fake_file;
832  delete texture_image; 
833
834  char name[256];
835  sprintf(name, "memory_image_%d", return_handle);
836  r1_texture_handle han=register_texture(name, name);
837  registered_tnames[han].handle=return_handle;
838
839  return han;
840}
841
842
843
844
845
Note: See TracBrowser for help on using the repository browser.