Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
HeightMipmap.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "HeightMipmap.h"
21 
22 #include "lib/bits.h"
23 #include "lib/timer.h"
25 #include "lib/tex/tex.h"
26 #include "maths/MathUtil.h"
27 #include "ps/Filesystem.h"
28 
29 #include <cmath>
30 
32 {
33 }
34 
36 {
37  ReleaseData();
38 }
39 
41 {
42  for (size_t i = 0; i < m_Mipmap.size(); ++i)
43  {
44  delete[] m_Mipmap[i].m_Heightmap;
45  m_Mipmap[i].m_MapSize = 0;
46  }
47  m_Mipmap.clear();
48 }
49 
50 void CHeightMipmap::Update(const u16* ptr)
51 {
52  ENSURE(ptr != 0);
53 
54  Update(ptr, 0, 0, m_MapSize, m_MapSize);
55 }
56 
57 void CHeightMipmap::Update(const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
58 {
59  ENSURE(ptr != 0);
60 
61  size_t mapSize = m_MapSize;
62 
63  for (size_t i = 0; i < m_Mipmap.size(); ++i)
64  {
65  // update window
66  left = clamp<size_t>((size_t)floorf((float)left / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1);
67  bottom = clamp<size_t>((size_t)floorf((float)bottom / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1);
68 
69  right = clamp<size_t>((size_t)ceilf((float)right / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize);
70  top = clamp<size_t>((size_t)ceilf((float)top / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize);
71 
72  // TODO: should verify that the bounds calculations are actually correct
73 
74  // update mipmap
75  BilinearUpdate(m_Mipmap[i], mapSize, ptr, left, bottom, right, top);
76 
77  mapSize = m_Mipmap[i].m_MapSize;
78  ptr = m_Mipmap[i].m_Heightmap;
79  }
80 }
81 
82 void CHeightMipmap::Initialize(size_t mapSize, const u16* ptr)
83 {
84  ENSURE(ptr != 0);
85  ENSURE(mapSize > 0);
86 
87  ReleaseData();
88 
89  m_MapSize = mapSize;
90  size_t mipmapSize = round_down_to_pow2(mapSize);
91 
92  while (mipmapSize > 1)
93  {
94  m_Mipmap.push_back(SMipmap(mipmapSize, new u16[mipmapSize*mipmapSize]));
95  mipmapSize >>= 1;
96  };
97 
98  Update(ptr);
99 }
100 
101 float CHeightMipmap::GetTrilinearGroundLevel(float x, float z, float radius) const
102 {
103  float y;
104  if (radius <= 0.0f) // avoid logf of non-positive value
105  y = 0.0f;
106  else
107  y = clamp<float>(logf(radius * m_Mipmap[0].m_MapSize) / logf(2), 0, m_Mipmap.size());
108 
109  const size_t iy = (size_t)clamp<ssize_t>((ssize_t)floorf(y), 0, m_Mipmap.size() - 2);
110 
111  const float fy = y - iy;
112 
113  const float h0 = BilinearFilter(m_Mipmap[iy], x, z);
114  const float h1 = BilinearFilter(m_Mipmap[iy + 1], x, z);
115 
116  return (1 - fy) * h0 + fy * h1;
117 }
118 
119 float CHeightMipmap::BilinearFilter(const SMipmap &mipmap, float x, float z) const
120 {
121  x *= mipmap.m_MapSize;
122  z *= mipmap.m_MapSize;
123 
124  const size_t xi = (size_t)clamp<ssize_t>((ssize_t)floor(x), 0, mipmap.m_MapSize - 2);
125  const size_t zi = (size_t)clamp<ssize_t>((ssize_t)floor(z), 0, mipmap.m_MapSize - 2);
126 
127  const float xf = clamp<float>(x-xi, 0.0f, 1.0f);
128  const float zf = clamp<float>(z-zi, 0.0f, 1.0f);
129 
130  const float h00 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + xi];
131  const float h01 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + xi];
132  const float h10 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + (xi+1)];
133  const float h11 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + (xi+1)];
134 
135  return
136  (1.f - xf) * (1.f - zf) * h00 +
137  xf * (1.f - zf) * h10 +
138  (1.f - xf) * zf * h01 +
139  xf * zf * h11;
140 }
141 
142 void CHeightMipmap::HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
143 {
144  // specialized, faster version of BilinearUpdate for powers of 2
145 
146  ENSURE(out_mipmap.m_MapSize != 0);
147 
148  if (out_mipmap.m_MapSize * 2 != mapSize)
149  debug_warn(L"wrong size");
150 
151  // valid update window
152  ENSURE(left < out_mipmap.m_MapSize);
153  ENSURE(bottom < out_mipmap.m_MapSize);
154  ENSURE(right > left && right <= out_mipmap.m_MapSize);
155  ENSURE(top > bottom && top <= out_mipmap.m_MapSize);
156 
157  for (size_t dstZ = bottom; dstZ < top; ++dstZ)
158  {
159  for (size_t dstX = left; dstX < right; ++dstX)
160  {
161  size_t srcX = dstX << 1;
162  size_t srcZ = dstZ << 1;
163 
164  u16 h00 = ptr[srcX + 0 + srcZ * mapSize];
165  u16 h10 = ptr[srcX + 1 + srcZ * mapSize];
166  u16 h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];
167  u16 h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];
168 
169  out_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (h00 + h10 + h01 + h11) / 4;
170  }
171  }
172 }
173 
174 void CHeightMipmap::BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
175 {
176  ENSURE(out_mipmap.m_MapSize != 0);
177 
178  // filter should have full coverage
179  ENSURE(out_mipmap.m_MapSize <= mapSize && out_mipmap.m_MapSize * 2 >= mapSize);
180 
181  // valid update window
182  ENSURE(left < out_mipmap.m_MapSize);
183  ENSURE(bottom < out_mipmap.m_MapSize);
184  ENSURE(right > left && right <= out_mipmap.m_MapSize);
185  ENSURE(top > bottom && top <= out_mipmap.m_MapSize);
186 
187  if (out_mipmap.m_MapSize * 2 == mapSize)
188  {
189  // optimized for powers of 2
190  HalfResizeUpdate(out_mipmap, mapSize, ptr, left, bottom, right, top);
191  }
192  else
193  {
194  for (size_t dstZ = bottom; dstZ < top; ++dstZ)
195  {
196  for (size_t dstX = left; dstX < right; ++dstX)
197  {
198  const float x = ((float)dstX / (float)out_mipmap.m_MapSize) * mapSize;
199  const float z = ((float)dstZ / (float)out_mipmap.m_MapSize) * mapSize;
200 
201  const size_t srcX = clamp<size_t>((size_t)x, 0, mapSize - 2);
202  const size_t srcZ = clamp<size_t>((size_t)z, 0, mapSize - 2);
203 
204  const float fx = clamp<float>(x - srcX, 0.0f, 1.0f);
205  const float fz = clamp<float>(z - srcZ, 0.0f, 1.0f);
206 
207  const float h00 = ptr[srcX + 0 + srcZ * mapSize];
208  const float h10 = ptr[srcX + 1 + srcZ * mapSize];
209  const float h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];
210  const float h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];
211 
212  out_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (u16)
213  ((1.f - fx) * (1.f - fz) * h00 +
214  fx * (1.f - fz) * h10 +
215  (1.f - fx) * fz * h01 +
216  fx * fz * h11);
217  }
218  }
219  }
220 }
221 
222 void CHeightMipmap::DumpToDisk(const VfsPath& filename) const
223 {
224  const size_t w = m_MapSize;
225  const size_t h = m_MapSize * 2;
226  const size_t bpp = 8;
227  int flags = TEX_GREY|TEX_TOP_DOWN;
228 
229  const size_t img_size = w * h * bpp/8;
230  const size_t hdr_size = tex_hdr_size(filename);
231  shared_ptr<u8> buf;
232  AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
233  void* img = buf.get() + hdr_size;
234  Tex t;
235  WARN_IF_ERR(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t));
236 
237  memset(img, 0x00, img_size);
238  size_t yoff = 0;
239  for (size_t i = 0; i < m_Mipmap.size(); ++i)
240  {
241  size_t size = m_Mipmap[i].m_MapSize;
242  u16* heightmap = m_Mipmap[i].m_Heightmap;
243  ENSURE(size+yoff <= h);
244  for (size_t y = 0; y < size; ++y)
245  {
246  for (size_t x = 0; x < size; ++x)
247  {
248  u16 val = heightmap[x + y*size];
249  ((u8*)img)[x + (y+yoff)*w] = val >> 8;
250  }
251  }
252  yoff += size;
253  }
254 
255  DynArray da;
256  WARN_IF_ERR(tex_encode(&t, filename.Extension(), &da));
257  g_VFS->CreateFile(filename, DummySharedPtr(da.base), da.pos);
258  (void)da_free(&da);
259 
260  tex_free(&t);
261 }
#define u8
Definition: types.h:39
void DumpToDisk(const VfsPath &path) const
#define WARN_IF_ERR(expression)
Definition: status.h:265
void ReleaseData()
float BilinearFilter(const SMipmap &mipmap, float x, float z) const
size_t m_MapSize
Definition: HeightMipmap.h:73
static const uintptr_t maxSectorSize
Definition: alignment.h:82
provides a memory range that can be expanded but doesn&#39;t waste physical memory or relocate itself...
Definition: dynarray.h:39
float GetTrilinearGroundLevel(float x, float z, float radius) const
indicates the image is 8bpp greyscale.
Definition: tex.h:178
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:703
void BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16 *ptr, size_t left, size_t bottom, size_t right, size_t top)
u16 * m_Heightmap
Definition: HeightMipmap.h:35
void HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16 *ptr, size_t left, size_t bottom, size_t right, size_t top)
void Initialize(size_t mapSize, const u16 *ptr)
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
u8 * base
Definition: dynarray.h:41
Definition: path.h:75
void Update(const u16 *ptr)
size_t m_MapSize
Definition: HeightMipmap.h:34
shared_ptr< T > DummySharedPtr(T *ptr)
Definition: shared_ptr.h:38
stores all data describing an image.
Definition: tex.h:210
size_t pos
Definition: dynarray.h:46
intptr_t ssize_t
Definition: wposix_types.h:82
std::vector< SMipmap > m_Mipmap
Definition: HeightMipmap.h:76
#define u16
Definition: types.h:40
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
Path Extension() const
Definition: path.h:176
Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr< u8 > &data, size_t ofs, Tex *t)
store the given image data into a Tex object; this will be as if it had been loaded via tex_load...
Definition: tex.cpp:593
Status da_free(DynArray *da)
free all memory (address space + physical) that constitutes the given array.
Definition: dynarray.cpp:80
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
T round_down_to_pow2(T x)
round down to next larger power of two.
Definition: bits.h:253
PIVFS g_VFS
Definition: Filesystem.cpp:30
void tex_free(Tex *t)
free all resources associated with the image and make further use of it impossible.
Definition: tex.cpp:610
Status tex_encode(Tex *t, const OsPath &extension, DynArray *da)
encode a texture into a memory buffer in the desired file format.
Definition: tex.cpp:750