atlas.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #include "atlas.h"
  2. struct AtlasRect { unsigned int x, y, w, h; };
  3. /* temp memory is freed by caller */
  4. enum AtlasResult atlasCompute(const struct AtlasContext* context) {
  5. const unsigned int max_rects_count = 1 + context->rects_count;
  6. unsigned int rects_count = 1;
  7. if (context->temp_storage.size < sizeof(struct AtlasRect) * max_rects_count)
  8. return Atlas_ErrorInsufficientTemp;
  9. struct AtlasRect *rects = context->temp_storage.ptr;
  10. rects[0].x = rects[0].y = 0;
  11. rects[0].w = context->width; rects[0].h = context->height;
  12. for (unsigned int i = 0; i < context->rects_count; ++i) {
  13. const struct AtlasVec * const item = (void*)((char*)context->rects + i * context->rects_stride);
  14. const unsigned int area = item->x * item->y;
  15. /* find best fit for this rect */
  16. struct AtlasRect *fit = 0;
  17. struct AtlasRect *first_empty = 0; /* optimization */
  18. unsigned int wasted_area = context->width * context->height;
  19. for (unsigned int j = 0; j < rects_count; ++j) {
  20. struct AtlasRect *const r = rects + j;
  21. if (!first_empty && (!r->w || !r->h)) first_empty = r;
  22. if (r->w < item->x || r->h < item->y)
  23. continue;
  24. /* exact match is the best */
  25. if (r->w == item->x && r->h == item->y) {
  26. fit = r;
  27. break;
  28. }
  29. const unsigned int r_area = r->w * r->h;
  30. const unsigned int fit_waste = r_area - area;
  31. if (!fit || ((r->w < fit->w || r->h < fit->h) && (fit_waste <= wasted_area))) {
  32. wasted_area = fit_waste;
  33. fit = r;
  34. }
  35. } /* find best fit in all empty rects */
  36. if (!fit)
  37. /* cannot allocate space for this lightmap fragment */
  38. return Atlas_ErrorDoesntFit;
  39. struct AtlasVec * const pos = (void*)((char*)context->pos + i * context->pos_stride);
  40. pos->x = fit->x;
  41. pos->y = fit->y;
  42. /* how to split */
  43. unsigned int rem_width = fit->w - item->x;
  44. unsigned int rem_height = fit->h - item->y;
  45. if (!rem_width && !rem_height) {
  46. fit->w = fit->h = 0;
  47. continue;
  48. }
  49. if (rem_width && rem_height && !first_empty)
  50. first_empty = rects + (rects_count++);
  51. /* split! */
  52. if (rem_width > rem_height) {
  53. if (rem_height) {
  54. first_empty->x = fit->x + item->x;
  55. first_empty->y = fit->y;
  56. first_empty->w = rem_width;
  57. first_empty->h = fit->h;
  58. fit->y += item->y;
  59. fit->w = item->x;
  60. fit->h = rem_height;
  61. } else {
  62. fit->x += item->x;
  63. fit->w = rem_width;
  64. }
  65. } else {
  66. if (rem_width) {
  67. first_empty->x = fit->x;
  68. first_empty->y = fit->y + item->y;
  69. first_empty->w = fit->w;
  70. first_empty->h = rem_height;
  71. fit->x += item->x;
  72. fit->w = rem_width;
  73. fit->h = item->y;
  74. } else {
  75. fit->y += item->y;
  76. fit->h = rem_height;
  77. }
  78. } /* split */
  79. } /* for all input rects */
  80. return Atlas_Success;
  81. }