Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
TerrainTextureEntry.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 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 "TerrainTextureEntry.h"
21 
22 #include "lib/utf8.h"
23 #include "lib/ogl.h"
26 
27 #include "ps/CLogger.h"
28 #include "ps/Filesystem.h"
29 #include "ps/XML/Xeromyces.h"
30 
32 #include "graphics/Terrain.h"
35 #include "graphics/Texture.h"
36 #include "renderer/Renderer.h"
37 
38 #include <map>
39 
41  m_pProperties(properties),
42  m_BaseColor(0),
43  m_BaseColorValid(false)
44 {
45  ENSURE(properties);
46 
47  CXeromyces XeroFile;
48  if (XeroFile.Load(g_VFS, path) != PSRETURN_OK)
49  {
50  LOGERROR(L"Terrain xml not found (%hs)", path.string().c_str());
51  return;
52  }
53 
54  #define EL(x) int el_##x = XeroFile.GetElementID(#x)
55  #define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
56  EL(tag);
57  EL(terrain);
58  EL(texture);
59  EL(textures);
60  EL(material);
61  EL(props);
62  EL(alphamap);
63  AT(file);
64  AT(name);
65  #undef AT
66  #undef EL
67 
68 
69  XMBElement root = XeroFile.GetRoot();
70 
71  if (root.GetNodeName() != el_terrain)
72  {
73  LOGERROR(L"Invalid terrain format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str());
74  return;
75  }
76 
77 
78  std::vector<std::pair<CStr, VfsPath> > samplers;
79  VfsPath alphamap("standard");
81 
82 
83  XERO_ITER_EL(root, child)
84  {
85  int child_name = child.GetNodeName();
86 
87  if (child_name == el_textures)
88  {
89  XERO_ITER_EL(child, textures_element)
90  {
91  ENSURE(textures_element.GetNodeName() == el_texture);
92 
93  CStr name;
94  VfsPath path;
95  XERO_ITER_ATTR(textures_element, se)
96  {
97  if (se.Name == at_file)
98  path = VfsPath("art/textures/terrain") / se.Value.FromUTF8();
99  else if (se.Name == at_name)
100  name = se.Value;
101  }
102  samplers.push_back(make_pair(name, path));
103  }
104 
105  }
106  else if (child_name == el_material)
107  {
108  VfsPath mat = VfsPath("art/materials") / child.GetText().FromUTF8();
110  m_Material = g_Renderer.GetMaterialManager().LoadMaterial(mat);
111  }
112  else if (child_name == el_alphamap)
113  {
114  alphamap = child.GetText().FromUTF8();
115  }
116  else if (child_name == el_props)
117  {
118  CTerrainPropertiesPtr ret (new CTerrainProperties(properties));
119  ret->LoadXml(child, &XeroFile, path);
120  if (ret) m_pProperties = ret;
121  }
122  else if (child_name == el_tag)
123  {
124  m_Tag = child.GetText();
125  }
126  }
127 
128 
129  for (size_t i = 0; i < samplers.size(); ++i)
130  {
131  CTextureProperties texture(samplers[i].second);
132  texture.SetWrap(GL_REPEAT);
133 
134  // TODO: anisotropy should probably be user-configurable, but we want it to be
135  // at least 2 for terrain else the ground looks very blurry when you tilt the
136  // camera upwards
137  texture.SetMaxAnisotropy(2.0f);
138 
140  {
141  CTexturePtr texptr = g_Renderer.GetTextureManager().CreateTexture(texture);
142  m_Material.AddSampler(CMaterial::TextureSampler(samplers[i].first, texptr));
143  }
144  }
145 
147  LoadAlphaMaps(alphamap);
148 
149  float texAngle = 0.f;
150  float texSize = 1.f;
151 
152  if (m_pProperties)
153  {
154  m_Groups = m_pProperties->GetGroups();
155  texAngle = m_pProperties->GetTextureAngle();
156  texSize = m_pProperties->GetTextureSize();
157  }
158 
160  m_TextureMatrix._11 = cosf(texAngle) / texSize;
161  m_TextureMatrix._13 = -sinf(texAngle) / texSize;
162  m_TextureMatrix._21 = -sinf(texAngle) / texSize;
163  m_TextureMatrix._23 = -cosf(texAngle) / texSize;
164  m_TextureMatrix._44 = 1.f;
165 
166  GroupVector::iterator it=m_Groups.begin();
167  for (;it!=m_Groups.end();++it)
168  (*it)->AddTerrain(this);
169 }
170 
172 {
173  for (GroupVector::iterator it=m_Groups.begin();it!=m_Groups.end();++it)
174  (*it)->RemoveTerrain(this);
175 }
176 
177 // BuildBaseColor: calculate the root colour of the texture, used for coloring minimap, and store
178 // in m_BaseColor member
180 {
181  // Use the explicit properties value if possible
182  if (m_pProperties && m_pProperties->HasBaseColor())
183  {
184  m_BaseColor=m_pProperties->GetBaseColor();
185  m_BaseColorValid = true;
186  return;
187  }
188 
189  // Use the texture colour if available
190  if (GetTexture()->TryLoad())
191  {
192  m_BaseColor = GetTexture()->GetBaseColour();
193  m_BaseColorValid = true;
194  }
195 }
196 
198 {
199  return &m_TextureMatrix._11;
200 }
201 
202 // LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
203 // calculate the coordinate of each alphamap within this packed texture
205 {
206  std::wstring key = L"(alpha map composite" + amtype.string() + L")";
207 
208  CTerrainTextureManager::TerrainAlphaMap::iterator it = g_TexMan.m_TerrainAlphas.find(amtype);
209 
210  if (it != g_TexMan.m_TerrainAlphas.end())
211  {
212  m_TerrainAlpha = it;
213  return;
214  }
215 
216  g_TexMan.m_TerrainAlphas[amtype] = TerrainAlpha();
217  it = g_TexMan.m_TerrainAlphas.find(amtype);
218 
219  TerrainAlpha &result = it->second;
220 
221  //
222  // load all textures and store Handle in array
223  //
224  Handle textures[NUM_ALPHA_MAPS] = {0};
225  VfsPath path(L"art/textures/terrain/alphamaps");
226  path = path / amtype;
227 
228  const wchar_t* fnames[NUM_ALPHA_MAPS] = {
229  L"blendcircle.png",
230  L"blendlshape.png",
231  L"blendedge.png",
232  L"blendedgecorner.png",
233  L"blendedgetwocorners.png",
234  L"blendfourcorners.png",
235  L"blendtwooppositecorners.png",
236  L"blendlshapecorner.png",
237  L"blendtwocorners.png",
238  L"blendcorner.png",
239  L"blendtwoedges.png",
240  L"blendthreecorners.png",
241  L"blendushape.png",
242  L"blendbad.png"
243  };
244  size_t base = 0; // texture width/height (see below)
245  // for convenience, we require all alpha maps to be of the same BPP
246  // (avoids another ogl_tex_get_size call, and doesn't hurt)
247  size_t bpp = 0;
248  for(size_t i=0;i<NUM_ALPHA_MAPS;i++)
249  {
250  // note: these individual textures can be discarded afterwards;
251  // we cache the composite.
252  textures[i] = ogl_tex_load(g_VFS, path / fnames[i]);
253  if (textures[i] < 0)
254  {
255  g_TexMan.m_TerrainAlphas.erase(it);
256  LOGERROR((L"Failed to load alphamap: " + amtype.string()).c_str());
257 
258  VfsPath standard("standard");
259  if (path != standard)
260  {
261  LoadAlphaMaps(standard);
262  }
263  return;
264  }
265 
266  // get its size and make sure they are all equal.
267  // (the packing algo assumes this)
268  size_t this_width = 0, this_height = 0, this_bpp = 0; // fail-safe
269  (void)ogl_tex_get_size(textures[i], &this_width, &this_height, &this_bpp);
270  if(this_width != this_height)
271  DEBUG_DISPLAY_ERROR(L"Alpha maps are not square");
272  // .. first iteration: establish size
273  if(i == 0)
274  {
275  base = this_width;
276  bpp = this_bpp;
277  }
278  // .. not first: make sure texture size matches
279  else if(base != this_width || bpp != this_bpp)
280  DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)");
281  }
282 
283  //
284  // copy each alpha map (tile) into one buffer, arrayed horizontally.
285  //
286  size_t tile_w = 2+base+2; // 2 pixel border (avoids bilinear filtering artifacts)
287  size_t total_w = round_up_to_pow2(tile_w * NUM_ALPHA_MAPS);
288  size_t total_h = base; ENSURE(is_pow2(total_h));
289  shared_ptr<u8> data;
290  AllocateAligned(data, total_w*total_h, maxSectorSize);
291  // for each tile on row
292  for (size_t i = 0; i < NUM_ALPHA_MAPS; i++)
293  {
294  // get src of copy
295  u8* src = 0;
296  (void)ogl_tex_get_data(textures[i], &src);
297 
298  size_t srcstep = bpp/8;
299 
300  // get destination of copy
301  u8* dst = data.get() + (i*tile_w);
302 
303  // for each row of image
304  for (size_t j = 0; j < base; j++)
305  {
306  // duplicate first pixel
307  *dst++ = *src;
308  *dst++ = *src;
309 
310  // copy a row
311  for (size_t k = 0; k < base; k++)
312  {
313  *dst++ = *src;
314  src += srcstep;
315  }
316 
317  // duplicate last pixel
318  *dst++ = *(src-srcstep);
319  *dst++ = *(src-srcstep);
320 
321  // advance write pointer for next row
322  dst += total_w-tile_w;
323  }
324 
325  result.m_AlphaMapCoords[i].u0 = float(i*tile_w+2) / float(total_w);
326  result.m_AlphaMapCoords[i].u1 = float((i+1)*tile_w-2) / float(total_w);
327  result.m_AlphaMapCoords[i].v0 = 0.0f;
328  result.m_AlphaMapCoords[i].v1 = 1.0f;
329  }
330 
331  for (size_t i = 0; i < NUM_ALPHA_MAPS; i++)
332  (void)ogl_tex_free(textures[i]);
333 
334  // upload the composite texture
335  Tex t;
336  (void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t);
337 
338  // uncomment the following to save a png of the generated texture
339  // in the public/ directory, for debugging
340  /*VfsPath filename("blendtex.png");
341 
342  DynArray da;
343  RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
344 
345  // write to disk
346  //Status ret = INFO::OK;
347  {
348  shared_ptr<u8> file = DummySharedPtr(da.base);
349  const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);
350  if(bytes_written > 0)
351  ENSURE(bytes_written == (ssize_t)da.pos);
352  //else
353  // ret = (Status)bytes_written;
354  }
355 
356  (void)da_free(&da);*/
357 
358  Handle hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
359  (void)ogl_tex_set_filter(hCompositeAlphaMap, GL_LINEAR);
360  (void)ogl_tex_set_wrap (hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
361  ogl_tex_upload(hCompositeAlphaMap, GL_ALPHA, 0, 0);
362  result.m_hCompositeAlphaMap = hCompositeAlphaMap;
363 
364  m_TerrainAlpha = it;
365 }
float _21
Definition: Matrix3D.h:42
struct TerrainAlpha::@16 m_AlphaMapCoords[NUM_ALPHA_MAPS]
#define u8
Definition: types.h:39
shared_ptr< CTerrainProperties > CTerrainPropertiesPtr
#define g_TexMan
Path VfsPath
VFS path of the form &quot;(dir/)*file?&quot;.
Definition: vfs_path.h:40
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Load from an XML file (with invisible XMB caching).
Definition: Xeromyces.cpp:65
#define LOGERROR
Definition: CLogger.h:35
#define XERO_ITER_ATTR(parent_element, attribute)
Definition: Xeromyces.h:99
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
float _44
Definition: Matrix3D.h:45
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
Definition: utf8.cpp:208
Handle ogl_tex_wrap(Tex *t, const PIVFS &vfs, const VfsPath &pathname, size_t flags)
Make the Tex object ready for use as an OpenGL texture and return a handle to it. ...
Definition: ogl_tex.cpp:570
Status ogl_tex_free(Handle &ht)
Release this texture reference.
Definition: ogl_tex.cpp:586
const float * GetTextureMatrix()
static const uintptr_t maxSectorSize
Definition: alignment.h:82
Represents the filename and GL parameters of a texture, for passing to CTextureManager::CreateTexture...
void AddSampler(const TextureSampler &texture)
Definition: Material.cpp:52
void SetZero()
Definition: Matrix3D.cpp:39
indicates the image is 8bpp greyscale.
Definition: tex.h:178
#define XERO_ITER_EL(parent_element, child_element)
Definition: Xeromyces.h:91
float _11
Definition: Matrix3D.h:42
#define NUM_ALPHA_MAPS
void LoadAlphaMaps(VfsPath &amtype)
Path Basename() const
Definition: path.h:166
#define g_Renderer
Definition: Renderer.h:61
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
T round_up_to_pow2(T x)
round up to next larger power of two.
Definition: bits.h:244
Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t)
Override default wrap mode (GL_REPEAT) for this texture.
Definition: ogl_tex.cpp:657
Definition: path.h:75
const String & string() const
Definition: path.h:123
float _13
Definition: Matrix3D.h:44
pthread_key_t key
Definition: wpthread.cpp:140
const CTexturePtr & GetTexture()
#define EL(x)
CTerrainTextureEntry(CTerrainPropertiesPtr props, const VfsPath &path)
i64 Handle
`handle&#39; representing a reference to a resource (sound, texture, etc.)
Definition: handle.h:41
static bool IsInitialised()
Definition: Singleton.h:63
stores all data describing an image.
Definition: tex.h:210
std::string GetElementString(const int ID) const
Definition: XeroXMB.cpp:148
Handle ogl_tex_load(const PIVFS &vfs, const VfsPath &pathname, size_t flags)
Load and return a handle to the texture.
Definition: ogl_tex.cpp:542
#define AT(x)
void SetMaxAnisotropy(float aniso)
Set maximum anisotropy value.
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
XMBElement GetRoot() const
Definition: XeroXMB.cpp:84
CTerrainPropertiesPtr m_pProperties
float _23
Definition: Matrix3D.h:44
Status ogl_tex_set_filter(Handle ht, GLint filter)
Override default filter (see ogl_tex_set_defaults) for this texture.
Definition: ogl_tex.cpp:638
Status ogl_tex_get_data(Handle ht, u8 **p)
Retrieve pixel data of the texture.
Definition: ogl_tex.cpp:1021
bool is_pow2(T n)
Definition: bits.h:164
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
#define DEBUG_DISPLAY_ERROR(description)
Definition: debug.h:197
PIVFS g_VFS
Definition: Filesystem.cpp:30
void SetWrap(GLint wrap)
Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
CTerrainTextureManager::TerrainAlphaMap::iterator m_TerrainAlpha
Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint int_fmt_ovr)
Upload texture to OpenGL.
Definition: ogl_tex.cpp:912
int GetNodeName() const
Definition: XeroXMB.cpp:166
Status ogl_tex_get_size(Handle ht, size_t *w, size_t *h, size_t *bpp)
Retrieve dimensions and bit depth of the texture.
Definition: ogl_tex.cpp:982
shared_ptr< CTexture > CTexturePtr
Definition: Texture.h:22