Panda3D
shadowAtlas.cxx
1 /**
2  *
3  * RenderPipeline
4  *
5  * Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  */
26 
27 
28 #include "shadowAtlas.h"
29 #include <string.h>
30 
31 NotifyCategoryDef(shadowatlas, "");
32 
33 /**
34  * @brief Constructs a new shadow atlas.
35  * @details This constructs a new shadow atlas with the given size and tile size.
36  *
37  * The size determines the total size of the atlas in pixels. It should be a
38  * power-of-two to favour the GPU.
39  *
40  * The tile_size determines the smallest unit of tiles the atlas can store.
41  * If, for example, a tile_size of 32 is used, then every entry stored must
42  * have a resolution of 32 or greater, and the resolution must be a multiple
43  * of 32. This is to optimize the search in the atlas, so the atlas does not
44  * have to check every pixel, and instead can just check whole tiles.
45  *
46  * If you want to disable the use of tiles, set the tile_size to 1, which
47  * will make the shadow atlas use pixels instead of tiles.
48  *
49  * @param size Atlas-size in pixels
50  * @param tile_size tile-size in pixels, or 1 to use no tiles.
51  */
52 ShadowAtlas::ShadowAtlas(size_t size, size_t tile_size) {
53  nassertv(size > 1 && tile_size >= 1);
54  nassertv(tile_size < size && size % tile_size == 0);
55  _size = size;
56  _tile_size = tile_size;
57  _num_used_tiles = 0;
58  init_tiles();
59 }
60 
61 /**
62  * @brief Destructs the shadow atlas.
63  * @details This destructs the shadow atlas, freeing all used resources.
64  */
66  delete [] _flags;
67 }
68 
69 /**
70  * @brief Internal method to init the storage.
71  * @details This method setups the storage used for storing the tile flags.
72  */
73 void ShadowAtlas::init_tiles() {
74  _num_tiles = _size / _tile_size;
75  _flags = new bool[_num_tiles * _num_tiles];
76  memset(_flags, 0x0, sizeof(bool) * _num_tiles * _num_tiles);
77 }
78 
79 /**
80  * @brief Internal method to reserve a region in the atlas.
81  * @details This reserves a given region in the shadow atlas. The region should
82  * be in tile space.This is called by the ShadowAtlas::find_and_reserve_region.
83  * It sets all flags in that region to true, indicating that those are used.
84  * When an invalid region is passed, an assertion is triggered. If assertions
85  * are optimized out, undefined behaviour occurs.
86  *
87  * @param x x- start positition of the region
88  * @param y y- start position of the region
89  * @param w width of the region
90  * @param h height of the region
91  */
92 void ShadowAtlas::reserve_region(size_t x, size_t y, size_t w, size_t h) {
93  // Check if we are out of bounds, this should be disabled for performance
94  // reasons at some point.
95  nassertv(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles);
96 
97  _num_used_tiles += w * h;
98 
99  // Iterate over every tile in the region and mark it as used
100  for (size_t cx = 0; cx < w; ++cx) {
101  for (size_t cy = 0; cy < h; ++cy) {
102  set_tile(cx + x, cy + y, true);
103  }
104  }
105 }
106 
107 /**
108  * @brief Finds space for a map of the given size in the atlas.
109  * @details This methods searches for a space to store a region of the given
110  * size in the atlas. tile_width and tile_height should be already in tile
111  * space. They can be converted using ShadowAtlas::get_required_tiles.
112  *
113  * If no region is found, or an invalid size is passed, an integer vector with
114  * all components set to -1 is returned.
115  *
116  * If a region is found, an integer vector with the given layout is returned:
117  * x: x- Start of the region
118  * y: y- Start of the region
119  * z: width of the region
120  * w: height of the region
121  *
122  * The layout is in tile space, and can get converted to uv space using
123  * ShadowAtlas::region_to_uv.
124  *
125  * @param tile_width Width of the region in tile space
126  * @param tile_height Height of the region in tile space
127  *
128  * @return Region, see description, or -1 when no region is found.
129  */
130 LVecBase4i ShadowAtlas::find_and_reserve_region(size_t tile_width, size_t tile_height) {
131 
132  // Check for empty region
133  if (tile_width < 1 || tile_height < 1) {
134  shadowatlas_cat.error() << "Called find_and_reserve_region with null-region!" << std::endl;
135  return LVecBase4i(-1);
136  }
137 
138  // Check for region bigger than the shadow atlas
139  if (tile_width > _num_tiles || tile_height > _num_tiles) {
140  shadowatlas_cat.error() << "Requested region exceeds shadow atlas size!" << std::endl;
141  return LVecBase4i(-1);
142  }
143 
144  // Iterate over every possible region and check if its still free
145  for (size_t x = 0; x <= _num_tiles - tile_width; ++x) {
146  for (size_t y = 0; y <= _num_tiles - tile_height; ++y) {
147  if (region_is_free(x, y, tile_width, tile_height)) {
148  // Found free region, now reserve it
149  reserve_region(x, y, tile_width, tile_height);
150  return LVecBase4i(x, y, tile_width, tile_height);
151  }
152  }
153  }
154 
155  // When we reached this part, we couldn't find a free region, so the atlas
156  // seems to be full.
157  shadowatlas_cat.error() << "Failed to find a free region of size " << tile_width
158  << " x " << tile_height << "!" << std::endl;
159  return LVecBase4i(-1);
160 }
161 
162 /**
163  * @brief Frees a given region
164  * @details This frees a given region, marking it as free so that other shadow
165  * maps can use the space again. The region should be the same as returned
166  * by ShadowAtlas::find_and_reserve_region.
167  *
168  * If an invalid region is passed, an assertion is triggered. If assertions
169  * are compiled out, undefined behaviour will occur.
170  *
171  * @param region Region to free
172  */
173 void ShadowAtlas::free_region(const LVecBase4i& region) {
174  // Out of bounds check, can't hurt
175  nassertv(region.get_x() >= 0 && region.get_y() >= 0);
176  nassertv(region.get_x() + region.get_z() <= (int)_num_tiles && region.get_y() + region.get_w() <= (int)_num_tiles);
177 
178  _num_used_tiles -= region.get_z() * region.get_w();
179 
180  for (int x = 0; x < region.get_z(); ++x) {
181  for (int y = 0; y < region.get_w(); ++y) {
182  // Could do an assert here, that the tile should have been used (=true) before
183  set_tile(region.get_x() + x, region.get_y() + y, false);
184  }
185  }
186 }
~ShadowAtlas()
Destructs the shadow atlas.
Definition: shadowAtlas.cxx:65
ShadowAtlas(size_t size, size_t tile_size=32)
Constructs a new shadow atlas.
Definition: shadowAtlas.cxx:52
void free_region(const LVecBase4i &region)
Frees a given region.
LVecBase4i find_and_reserve_region(size_t tile_width, size_t tile_height)
Finds space for a map of the given size in the atlas.