1
0
forked from 0ad/0ad
0ad/source/graphics/TerrainTextureEntry.cpp
Itms cc67d54aeb Fix compiler warnings on VS 2015, refs #3439, refs #5069.
Patch By: Stan
Reviewed By: Itms, vladislavbelov
Differential Revision: https://code.wildfiregames.com/D1262
This was SVN commit r21480.
2018-03-10 09:58:53 +00:00

366 lines
9.7 KiB
C++

/* Copyright (C) 2018 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "TerrainTextureEntry.h"
#include "lib/utf8.h"
#include "lib/ogl.h"
#include "lib/allocators/shared_ptr.h"
#include "lib/res/graphics/ogl_tex.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "graphics/MaterialManager.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureManager.h"
#include "graphics/TerrainProperties.h"
#include "graphics/Texture.h"
#include "renderer/Renderer.h"
#include <map>
CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, const VfsPath& path):
m_pProperties(properties),
m_BaseColor(0),
m_BaseColorValid(false)
{
ENSURE(properties);
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, path, "terrain_texture") != PSRETURN_OK)
{
LOGERROR("Terrain xml not found (%s)", path.string8());
return;
}
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(tag);
EL(terrain);
EL(texture);
EL(textures);
EL(material);
EL(props);
EL(alphamap);
AT(file);
AT(name);
#undef AT
#undef EL
XMBElement root = XeroFile.GetRoot();
if (root.GetNodeName() != el_terrain)
{
LOGERROR("Invalid terrain format (unrecognised root element '%s')", XeroFile.GetElementString(root.GetNodeName()).c_str());
return;
}
std::vector<std::pair<CStr, VfsPath> > samplers;
VfsPath alphamap("standard");
m_Tag = utf8_from_wstring(path.Basename().string());
XERO_ITER_EL(root, child)
{
int child_name = child.GetNodeName();
if (child_name == el_textures)
{
XERO_ITER_EL(child, textures_element)
{
ENSURE(textures_element.GetNodeName() == el_texture);
CStr name;
VfsPath terrainTexturePath;
XERO_ITER_ATTR(textures_element, relativePath)
{
if (relativePath.Name == at_file)
terrainTexturePath = VfsPath("art/textures/terrain") / relativePath.Value.FromUTF8();
else if (relativePath.Name == at_name)
name = relativePath.Value;
}
samplers.emplace_back(name, terrainTexturePath);
}
}
else if (child_name == el_material)
{
VfsPath mat = VfsPath("art/materials") / child.GetText().FromUTF8();
if (CRenderer::IsInitialised())
m_Material = g_Renderer.GetMaterialManager().LoadMaterial(mat);
}
else if (child_name == el_alphamap)
{
alphamap = child.GetText().FromUTF8();
}
else if (child_name == el_props)
{
CTerrainPropertiesPtr ret (new CTerrainProperties(properties));
ret->LoadXml(child, &XeroFile, path);
if (ret) m_pProperties = ret;
}
else if (child_name == el_tag)
{
m_Tag = child.GetText();
}
}
for (size_t i = 0; i < samplers.size(); ++i)
{
CTextureProperties texture(samplers[i].second);
texture.SetWrap(GL_REPEAT);
// TODO: anisotropy should probably be user-configurable, but we want it to be
// at least 2 for terrain else the ground looks very blurry when you tilt the
// camera upwards
texture.SetMaxAnisotropy(2.0f);
if (CRenderer::IsInitialised())
{
CTexturePtr texptr = g_Renderer.GetTextureManager().CreateTexture(texture);
m_Material.AddSampler(CMaterial::TextureSampler(samplers[i].first, texptr));
}
}
if (CRenderer::IsInitialised())
LoadAlphaMaps(alphamap);
float texAngle = 0.f;
float texSize = 1.f;
if (m_pProperties)
{
m_Groups = m_pProperties->GetGroups();
texAngle = m_pProperties->GetTextureAngle();
texSize = m_pProperties->GetTextureSize();
}
m_TextureMatrix.SetZero();
m_TextureMatrix._11 = cosf(texAngle) / texSize;
m_TextureMatrix._13 = -sinf(texAngle) / texSize;
m_TextureMatrix._21 = -sinf(texAngle) / texSize;
m_TextureMatrix._23 = -cosf(texAngle) / texSize;
m_TextureMatrix._44 = 1.f;
GroupVector::iterator it=m_Groups.begin();
for (;it!=m_Groups.end();++it)
(*it)->AddTerrain(this);
}
CTerrainTextureEntry::~CTerrainTextureEntry()
{
for (GroupVector::iterator it=m_Groups.begin();it!=m_Groups.end();++it)
(*it)->RemoveTerrain(this);
}
// BuildBaseColor: calculate the root color of the texture, used for coloring minimap, and store
// in m_BaseColor member
void CTerrainTextureEntry::BuildBaseColor()
{
// Use the explicit properties value if possible
if (m_pProperties && m_pProperties->HasBaseColor())
{
m_BaseColor=m_pProperties->GetBaseColor();
m_BaseColorValid = true;
return;
}
// Use the texture color if available
if (GetTexture()->TryLoad())
{
m_BaseColor = GetTexture()->GetBaseColor();
m_BaseColorValid = true;
}
}
const float* CTerrainTextureEntry::GetTextureMatrix()
{
return &m_TextureMatrix._11;
}
// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
// calculate the coordinate of each alphamap within this packed texture
void CTerrainTextureEntry::LoadAlphaMaps(VfsPath &amtype)
{
std::wstring key = L"(alpha map composite" + amtype.string() + L")";
CTerrainTextureManager::TerrainAlphaMap::iterator it = g_TexMan.m_TerrainAlphas.find(amtype);
if (it != g_TexMan.m_TerrainAlphas.end())
{
m_TerrainAlpha = it;
return;
}
g_TexMan.m_TerrainAlphas[amtype] = TerrainAlpha();
it = g_TexMan.m_TerrainAlphas.find(amtype);
TerrainAlpha &result = it->second;
//
// load all textures and store Handle in array
//
Handle textures[NUM_ALPHA_MAPS] = {0};
VfsPath path(L"art/textures/terrain/alphamaps");
path = path / amtype;
const wchar_t* fnames[NUM_ALPHA_MAPS] = {
L"blendcircle.png",
L"blendlshape.png",
L"blendedge.png",
L"blendedgecorner.png",
L"blendedgetwocorners.png",
L"blendfourcorners.png",
L"blendtwooppositecorners.png",
L"blendlshapecorner.png",
L"blendtwocorners.png",
L"blendcorner.png",
L"blendtwoedges.png",
L"blendthreecorners.png",
L"blendushape.png",
L"blendbad.png"
};
size_t base = 0; // texture width/height (see below)
// for convenience, we require all alpha maps to be of the same BPP
// (avoids another ogl_tex_get_size call, and doesn't hurt)
size_t bpp = 0;
for(size_t i=0;i<NUM_ALPHA_MAPS;i++)
{
// note: these individual textures can be discarded afterwards;
// we cache the composite.
textures[i] = ogl_tex_load(g_VFS, path / fnames[i]);
if (textures[i] < 0)
{
g_TexMan.m_TerrainAlphas.erase(it);
LOGERROR("Failed to load alphamap: %s", amtype.string8());
VfsPath standard("standard");
if (path != standard)
{
LoadAlphaMaps(standard);
}
return;
}
// get its size and make sure they are all equal.
// (the packing algo assumes this)
size_t this_width = 0, this_height = 0, this_bpp = 0; // fail-safe
(void)ogl_tex_get_size(textures[i], &this_width, &this_height, &this_bpp);
if(this_width != this_height)
DEBUG_DISPLAY_ERROR(L"Alpha maps are not square");
// .. first iteration: establish size
if(i == 0)
{
base = this_width;
bpp = this_bpp;
}
// .. not first: make sure texture size matches
else if(base != this_width || bpp != this_bpp)
DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)");
}
//
// copy each alpha map (tile) into one buffer, arrayed horizontally.
//
size_t tile_w = 2+base+2; // 2 pixel border (avoids bilinear filtering artifacts)
size_t total_w = round_up_to_pow2(tile_w * NUM_ALPHA_MAPS);
size_t total_h = base; ENSURE(is_pow2(total_h));
shared_ptr<u8> data;
AllocateAligned(data, total_w*total_h, maxSectorSize);
// for each tile on row
for (size_t i = 0; i < NUM_ALPHA_MAPS; i++)
{
// get src of copy
u8* src = 0;
(void)ogl_tex_get_data(textures[i], &src);
size_t srcstep = bpp/8;
// get destination of copy
u8* dst = data.get() + (i*tile_w);
// for each row of image
for (size_t j = 0; j < base; j++)
{
// duplicate first pixel
*dst++ = *src;
*dst++ = *src;
// copy a row
for (size_t k = 0; k < base; k++)
{
*dst++ = *src;
src += srcstep;
}
// duplicate last pixel
*dst++ = *(src-srcstep);
*dst++ = *(src-srcstep);
// advance write pointer for next row
dst += total_w-tile_w;
}
result.m_AlphaMapCoords[i].u0 = float(i*tile_w+2) / float(total_w);
result.m_AlphaMapCoords[i].u1 = float((i+1)*tile_w-2) / float(total_w);
result.m_AlphaMapCoords[i].v0 = 0.0f;
result.m_AlphaMapCoords[i].v1 = 1.0f;
}
for (size_t i = 0; i < NUM_ALPHA_MAPS; i++)
(void)ogl_tex_free(textures[i]);
// upload the composite texture
Tex t;
(void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);
// uncomment the following to save a png of the generated texture
// in the public/ directory, for debugging
/*VfsPath filename("blendtex.png");
DynArray da;
RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
// write to disk
//Status ret = INFO::OK;
{
shared_ptr<u8> file = DummySharedPtr(da.base);
const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);
if(bytes_written > 0)
ENSURE(bytes_written == (ssize_t)da.pos);
//else
// ret = (Status)bytes_written;
}
(void)da_free(&da);*/
Handle hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
(void)ogl_tex_set_filter(hCompositeAlphaMap, GL_LINEAR);
(void)ogl_tex_set_wrap (hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
ogl_tex_upload(hCompositeAlphaMap, GL_ALPHA, 0, 0);
result.m_hCompositeAlphaMap = hCompositeAlphaMap;
m_TerrainAlpha = it;
}