2025-04-10 21:24:32 -07:00
|
|
|
/* Copyright (C) 2025 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2009-04-18 10:00:33 -07:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2009-04-18 10:00:33 -07:00
|
|
|
* 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.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2009-04-18 10:00:33 -07:00
|
|
|
* 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
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2009-04-18 10:00:33 -07:00
|
|
|
*/
|
|
|
|
|
|
2004-06-08 05:10:51 -07:00
|
|
|
#include "precompiled.h"
|
2005-06-20 08:14:36 -07:00
|
|
|
|
2012-01-22 10:20:33 -08:00
|
|
|
#include "TerrainTextureEntry.h"
|
2005-06-20 08:14:36 -07:00
|
|
|
|
2021-12-26 12:39:13 -08:00
|
|
|
#include "graphics/MaterialManager.h"
|
|
|
|
|
#include "graphics/Terrain.h"
|
|
|
|
|
#include "graphics/TerrainProperties.h"
|
|
|
|
|
#include "graphics/TerrainTextureManager.h"
|
2022-01-07 06:33:54 -08:00
|
|
|
#include "graphics/TextureManager.h"
|
2021-12-26 12:39:13 -08:00
|
|
|
#include "lib/utf8.h"
|
2012-08-07 11:21:16 -07:00
|
|
|
#include "ps/CLogger.h"
|
2021-12-30 08:24:07 -08:00
|
|
|
#include "ps/CStrInternStatic.h"
|
2012-08-07 11:21:16 -07:00
|
|
|
#include "ps/Filesystem.h"
|
|
|
|
|
#include "ps/XML/Xeromyces.h"
|
2006-06-01 19:10:27 -07:00
|
|
|
#include "renderer/Renderer.h"
|
2022-01-04 05:29:01 -08:00
|
|
|
#include "renderer/SceneRenderer.h"
|
2004-06-08 05:10:51 -07:00
|
|
|
|
2012-01-22 10:20:33 -08:00
|
|
|
#include <map>
|
|
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, const VfsPath& path):
|
|
|
|
|
m_pProperties(properties),
|
2005-06-20 08:14:36 -07:00
|
|
|
m_BaseColor(0),
|
2010-09-10 14:02:10 -07:00
|
|
|
m_BaseColorValid(false)
|
2004-12-16 04:01:47 -08:00
|
|
|
{
|
2012-08-07 11:21:16 -07:00
|
|
|
ENSURE(properties);
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
CXeromyces XeroFile;
|
2015-06-07 14:56:52 -07:00
|
|
|
if (XeroFile.Load(g_VFS, path, "terrain_texture") != PSRETURN_OK)
|
2012-08-07 11:21:16 -07:00
|
|
|
{
|
2015-01-22 12:36:24 -08:00
|
|
|
LOGERROR("Terrain xml not found (%s)", path.string8());
|
2012-08-07 11:21:16 -07:00
|
|
|
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
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
XMBElement root = XeroFile.GetRoot();
|
|
|
|
|
|
|
|
|
|
if (root.GetNodeName() != el_terrain)
|
|
|
|
|
{
|
2025-04-10 21:24:32 -07:00
|
|
|
LOGERROR("Invalid terrain format (unrecognized root element '%s')", XeroFile.GetElementString(root.GetNodeName()));
|
2012-08-07 11:21:16 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
std::vector<std::pair<CStr, VfsPath> > samplers;
|
|
|
|
|
VfsPath alphamap("standard");
|
|
|
|
|
m_Tag = utf8_from_wstring(path.Basename().string());
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
XERO_ITER_EL(root, child)
|
|
|
|
|
{
|
|
|
|
|
int child_name = child.GetNodeName();
|
2011-02-05 05:13:57 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
if (child_name == el_textures)
|
|
|
|
|
{
|
|
|
|
|
XERO_ITER_EL(child, textures_element)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(textures_element.GetNodeName() == el_texture);
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
CStr name;
|
2018-03-10 01:58:53 -08:00
|
|
|
VfsPath terrainTexturePath;
|
|
|
|
|
XERO_ITER_ATTR(textures_element, relativePath)
|
2012-08-07 11:21:16 -07:00
|
|
|
{
|
2018-03-10 01:58:53 -08:00
|
|
|
if (relativePath.Name == at_file)
|
|
|
|
|
terrainTexturePath = VfsPath("art/textures/terrain") / relativePath.Value.FromUTF8();
|
|
|
|
|
else if (relativePath.Name == at_name)
|
|
|
|
|
name = relativePath.Value;
|
2012-08-07 11:21:16 -07:00
|
|
|
}
|
2018-03-10 01:58:53 -08:00
|
|
|
samplers.emplace_back(name, terrainTexturePath);
|
2021-12-30 08:24:07 -08:00
|
|
|
if (name == str_baseTex.string())
|
|
|
|
|
m_DiffuseTexturePath = terrainTexturePath;
|
2012-08-07 11:21:16 -07:00
|
|
|
}
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
}
|
|
|
|
|
else if (child_name == el_material)
|
|
|
|
|
{
|
|
|
|
|
VfsPath mat = VfsPath("art/materials") / child.GetText().FromUTF8();
|
2012-12-06 11:23:31 -08:00
|
|
|
if (CRenderer::IsInitialised())
|
2022-01-04 05:29:01 -08:00
|
|
|
m_Material = g_Renderer.GetSceneRenderer().GetMaterialManager().LoadMaterial(mat);
|
2012-08-07 11:21:16 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2024-01-21 06:29:30 -08:00
|
|
|
m_pProperties = ret;
|
2012-08-07 11:21:16 -07:00
|
|
|
}
|
|
|
|
|
else if (child_name == el_tag)
|
|
|
|
|
{
|
|
|
|
|
m_Tag = child.GetText();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
for (size_t i = 0; i < samplers.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
CTextureProperties texture(samplers[i].second);
|
2022-02-13 11:30:28 -08:00
|
|
|
texture.SetAddressMode(Renderer::Backend::Sampler::AddressMode::REPEAT);
|
|
|
|
|
texture.SetAnisotropicFilter(true);
|
2010-09-10 14:02:10 -07:00
|
|
|
|
2012-08-07 11:21:16 -07:00
|
|
|
if (CRenderer::IsInitialised())
|
|
|
|
|
{
|
|
|
|
|
CTexturePtr texptr = g_Renderer.GetTextureManager().CreateTexture(texture);
|
|
|
|
|
m_Material.AddSampler(CMaterial::TextureSampler(samplers[i].first, texptr));
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-12-06 11:23:31 -08:00
|
|
|
|
|
|
|
|
if (CRenderer::IsInitialised())
|
2022-01-07 06:33:54 -08:00
|
|
|
m_TerrainAlpha = g_TexMan.LoadAlphaMap(alphamap);
|
2010-09-10 14:02:10 -07:00
|
|
|
|
2025-05-29 17:31:23 -07:00
|
|
|
float texAngle{0.f};
|
|
|
|
|
float texSize{1.f};
|
2012-01-22 10:20:33 -08:00
|
|
|
|
2005-08-07 14:58:36 -07:00
|
|
|
if (m_pProperties)
|
2012-01-22 10:20:33 -08:00
|
|
|
{
|
2005-08-07 14:58:36 -07:00
|
|
|
m_Groups = m_pProperties->GetGroups();
|
2012-01-22 10:20:33 -08:00
|
|
|
texAngle = m_pProperties->GetTextureAngle();
|
|
|
|
|
texSize = m_pProperties->GetTextureSize();
|
|
|
|
|
}
|
2025-05-29 17:31:23 -07:00
|
|
|
GenerateTextureMatrix(texAngle, texSize);
|
2012-01-22 10:20:33 -08:00
|
|
|
|
2005-06-20 08:14:36 -07:00
|
|
|
GroupVector::iterator it=m_Groups.begin();
|
|
|
|
|
for (;it!=m_Groups.end();++it)
|
2005-08-07 14:58:36 -07:00
|
|
|
(*it)->AddTerrain(this);
|
2005-06-20 08:14:36 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-29 17:31:23 -07:00
|
|
|
CTerrainTextureEntry::CTerrainTextureEntry(const CStr tag):
|
|
|
|
|
m_pProperties(nullptr),
|
|
|
|
|
m_BaseColor(0),
|
|
|
|
|
m_BaseColorValid(false),
|
|
|
|
|
m_DiffuseTexturePath(""),
|
|
|
|
|
m_Tag(tag)
|
|
|
|
|
{
|
|
|
|
|
if (!CRenderer::IsInitialised())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const VfsPath alphamap{"standard"};
|
|
|
|
|
const VfsPath mat{VfsPath{"art/materials"} / "terrain_norm_spec.xml"};
|
|
|
|
|
m_Material = g_Renderer.GetSceneRenderer().GetMaterialManager().LoadMaterial(mat);
|
|
|
|
|
const CTexturePtr texptr{g_Renderer.GetTextureManager().GetErrorTexture()};
|
|
|
|
|
m_Material.AddSampler(CMaterial::TextureSampler{str_baseTex, texptr});
|
|
|
|
|
m_TerrainAlpha = g_TexMan.LoadAlphaMap(alphamap);
|
|
|
|
|
|
|
|
|
|
GenerateTextureMatrix(0.0f, 1.f);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-04 14:26:52 -07:00
|
|
|
CTerrainTextureEntry::~CTerrainTextureEntry()
|
2005-06-20 08:14:36 -07:00
|
|
|
{
|
2005-08-07 14:58:36 -07:00
|
|
|
for (GroupVector::iterator it=m_Groups.begin();it!=m_Groups.end();++it)
|
|
|
|
|
(*it)->RemoveTerrain(this);
|
2004-12-16 04:01:47 -08:00
|
|
|
}
|
|
|
|
|
|
2015-03-15 16:59:48 -07:00
|
|
|
// BuildBaseColor: calculate the root color of the texture, used for coloring minimap, and store
|
2004-06-08 05:10:51 -07:00
|
|
|
// in m_BaseColor member
|
2010-09-04 14:26:52 -07:00
|
|
|
void CTerrainTextureEntry::BuildBaseColor()
|
2004-06-08 05:10:51 -07:00
|
|
|
{
|
2010-09-10 14:02:10 -07:00
|
|
|
// Use the explicit properties value if possible
|
2005-08-07 14:58:36 -07:00
|
|
|
if (m_pProperties && m_pProperties->HasBaseColor())
|
2005-06-20 08:14:36 -07:00
|
|
|
{
|
2005-08-07 14:58:36 -07:00
|
|
|
m_BaseColor=m_pProperties->GetBaseColor();
|
2010-09-10 14:02:10 -07:00
|
|
|
m_BaseColorValid = true;
|
2005-06-20 08:14:36 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-15 16:59:48 -07:00
|
|
|
// Use the texture color if available
|
2012-08-07 11:21:16 -07:00
|
|
|
if (GetTexture()->TryLoad())
|
2010-09-10 14:02:10 -07:00
|
|
|
{
|
2015-03-15 16:59:48 -07:00
|
|
|
m_BaseColor = GetTexture()->GetBaseColor();
|
2010-09-10 14:02:10 -07:00
|
|
|
m_BaseColorValid = true;
|
|
|
|
|
}
|
2004-06-10 19:14:18 -07:00
|
|
|
}
|
2025-05-29 17:31:23 -07:00
|
|
|
|
|
|
|
|
void CTerrainTextureEntry::GenerateTextureMatrix(const float texAngle, const float texSize)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|