Merge 'remotes/trunk'
[0ad.git] / source / renderer / SkyManager.cpp
blob45b3807357f6995280bb824e249b45b9a48eaaaf
1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "renderer/SkyManager.h"
22 #include "graphics/LightEnv.h"
23 #include "graphics/ShaderManager.h"
24 #include "graphics/Terrain.h"
25 #include "graphics/TextureManager.h"
26 #include "lib/bits.h"
27 #include "lib/tex/tex.h"
28 #include "maths/MathUtil.h"
29 #include "ps/CLogger.h"
30 #include "ps/ConfigDB.h"
31 #include "ps/CStr.h"
32 #include "ps/CStrInternStatic.h"
33 #include "ps/Filesystem.h"
34 #include "ps/Game.h"
35 #include "renderer/backend/IDevice.h"
36 #include "renderer/Renderer.h"
37 #include "renderer/SceneRenderer.h"
38 #include "renderer/RenderingOptions.h"
40 #include <algorithm>
42 SkyManager::SkyManager()
43 : m_VertexArray(Renderer::Backend::IBuffer::Type::VERTEX, false)
45 CFG_GET_VAL("showsky", m_SkyVisible);
48 void SkyManager::LoadAndUploadSkyTexturesIfNeeded(
49 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
51 if (m_VertexArray.GetNumberOfVertices() == 0)
52 CreateSkyCube();
54 if (m_SkyTextureCube)
55 return;
57 m_SkyTextureCube = g_Renderer.GetTextureManager().GetBlackTextureCube();
59 GPU_SCOPED_LABEL(deviceCommandContext, "Load Sky Textures");
60 static const CStrW images[NUMBER_OF_TEXTURES + 1] = {
61 L"front",
62 L"back",
63 L"top",
64 L"top",
65 L"right",
66 L"left"
69 /*for (size_t i = 0; i < ARRAY_SIZE(m_SkyTexture); ++i)
71 VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(s_imageNames[i])+L".dds");
73 CTextureProperties textureProps(path);
74 textureProps.SetWrap(GL_CLAMP_TO_EDGE);
75 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
76 texture->Prefetch();
77 m_SkyTexture[i] = texture;
78 }*/
80 ///////////////////////////////////////////////////////////////////////////
81 // HACK: THE HORRIBLENESS HERE IS OVER 9000. The following code is a HUGE hack and will be removed completely
82 // as soon as all the hardcoded GL_TEXTURE_2D references are corrected in the TextureManager/OGL/tex libs.
84 Tex textures[NUMBER_OF_TEXTURES + 1];
86 for (size_t i = 0; i < NUMBER_OF_TEXTURES + 1; ++i)
88 VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i]) + L".dds");
90 std::shared_ptr<u8> file;
91 size_t fileSize;
92 if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
94 path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i]) + L".dds.cached.dds");
95 if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
97 LOGERROR("Error creating sky cubemap '%s', can't load file: '%s'.", m_SkySet.ToUTF8().c_str(), path.string8().c_str());
98 return;
102 if (textures[i].decode(file, fileSize) != INFO::OK ||
103 textures[i].transform_to((textures[i].m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) != INFO::OK)
105 LOGERROR("Error creating sky cubemap '%s', can't decode file: '%s'.", m_SkySet.ToUTF8().c_str(), path.string8().c_str());
106 return;
109 if (!is_pow2(textures[i].m_Width) || !is_pow2(textures[i].m_Height))
111 LOGERROR("Error creating sky cubemap '%s', cube textures should have power of 2 sizes.", m_SkySet.ToUTF8().c_str());
112 return;
115 if (textures[i].m_Width != textures[0].m_Width || textures[i].m_Height != textures[0].m_Height)
117 LOGERROR("Error creating sky cubemap '%s', cube textures have different sizes.", m_SkySet.ToUTF8().c_str());
118 return;
122 std::unique_ptr<Renderer::Backend::ITexture> skyCubeMap =
123 deviceCommandContext->GetDevice()->CreateTexture("SkyCubeMap",
124 Renderer::Backend::ITexture::Type::TEXTURE_CUBE,
125 Renderer::Backend::ITexture::Usage::TRANSFER_DST |
126 Renderer::Backend::ITexture::Usage::SAMPLED,
127 Renderer::Backend::Format::R8G8B8A8_UNORM, textures[0].m_Width, textures[0].m_Height,
128 Renderer::Backend::Sampler::MakeDefaultSampler(
129 Renderer::Backend::Sampler::Filter::LINEAR,
130 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, 1);
132 std::vector<u8> rotated;
133 for (size_t i = 0; i < NUMBER_OF_TEXTURES + 1; ++i)
135 u8* data = textures[i].get_data();
137 // We need to rotate the side if it's looking up or down.
138 // TODO: maybe it should be done during texture conversion.
139 if (i == 2 || i == 3)
141 rotated.resize(textures[i].m_DataSize);
143 for (size_t y = 0; y < textures[i].m_Height; ++y)
145 for (size_t x = 0; x < textures[i].m_Width; ++x)
147 const size_t invX = y;
148 const size_t invY = textures[i].m_Width - x - 1;
150 rotated[(y * textures[i].m_Width + x) * 4 + 0] = data[(invY * textures[i].m_Width + invX) * 4 + 0];
151 rotated[(y * textures[i].m_Width + x) * 4 + 1] = data[(invY * textures[i].m_Width + invX) * 4 + 1];
152 rotated[(y * textures[i].m_Width + x) * 4 + 2] = data[(invY * textures[i].m_Width + invX) * 4 + 2];
153 rotated[(y * textures[i].m_Width + x) * 4 + 3] = data[(invY * textures[i].m_Width + invX) * 4 + 3];
157 deviceCommandContext->UploadTexture(
158 skyCubeMap.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
159 &rotated[0], textures[i].m_DataSize, 0, i);
161 else
163 deviceCommandContext->UploadTexture(
164 skyCubeMap.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
165 data, textures[i].m_DataSize, 0, i);
169 m_SkyTextureCube = g_Renderer.GetTextureManager().WrapBackendTexture(std::move(skyCubeMap));
170 ///////////////////////////////////////////////////////////////////////////
173 Renderer::Backend::ITexture* SkyManager::GetSkyCube()
175 return m_SkyTextureCube->GetBackendTexture();
178 void SkyManager::SetSkySet(const CStrW& newSet)
180 if (newSet == m_SkySet)
181 return;
183 m_SkyTextureCube.reset();
185 m_SkySet = newSet;
188 std::vector<CStrW> SkyManager::GetSkySets() const
190 std::vector<CStrW> skies;
192 // Find all subdirectories in art/textures/skies
194 const VfsPath path(L"art/textures/skies/");
195 DirectoryNames subdirectories;
196 if (g_VFS->GetDirectoryEntries(path, 0, &subdirectories) != INFO::OK)
198 LOGERROR("Error opening directory '%s'", path.string8());
199 return std::vector<CStrW>(1, GetSkySet()); // just return what we currently have
202 for(size_t i = 0; i < subdirectories.size(); i++)
203 skies.push_back(subdirectories[i].string());
204 sort(skies.begin(), skies.end());
206 return skies;
209 void SkyManager::RenderSky(
210 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
212 GPU_SCOPED_LABEL(deviceCommandContext, "Render sky");
214 const CTexturePtr& skyTextureCube =
215 !m_SkyVisible || m_SkySet.empty() || !m_SkyTextureCube
216 ? g_Renderer.GetTextureManager().GetBlackTextureCube()
217 : m_SkyTextureCube;
219 const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera();
221 CShaderTechniquePtr skytech =
222 g_Renderer.GetShaderManager().LoadEffect(str_sky_simple);
223 deviceCommandContext->SetGraphicsPipelineState(
224 skytech->GetGraphicsPipelineState());
225 deviceCommandContext->BeginPass();
226 Renderer::Backend::IShaderProgram* shader = skytech->GetShader();
227 deviceCommandContext->SetTexture(
228 shader->GetBindingSlot(str_baseTex), skyTextureCube->GetBackendTexture());
230 // Translate so the sky center is at the camera space origin.
231 CMatrix3D translate;
232 translate.SetTranslation(camera.GetOrientation().GetTranslation());
234 // Currently we have a hardcoded near plane in the projection matrix.
235 CMatrix3D scale;
236 scale.SetScaling(10.0f, 10.0f, 10.0f);
238 // Rotate so that the "left" face, which contains the brightest part of
239 // each skymap, is in the direction of the sun from our light
240 // environment.
241 CMatrix3D rotate;
242 rotate.SetYRotation(M_PI + g_Renderer.GetSceneRenderer().GetLightEnv().GetRotation());
244 const CMatrix3D transform = camera.GetViewProjection() * translate * rotate * scale;
245 deviceCommandContext->SetUniform(
246 shader->GetBindingSlot(str_transform), transform.AsFloatArray());
248 const uint32_t stride = m_VertexArray.GetStride();
249 const uint32_t firstVertexOffset = m_VertexArray.GetOffset() * stride;
251 deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
253 deviceCommandContext->SetVertexBuffer(
254 0, m_VertexArray.GetBuffer(), firstVertexOffset);
256 deviceCommandContext->Draw(0, m_VertexArray.GetNumberOfVertices());
258 deviceCommandContext->EndPass();
261 void SkyManager::CreateSkyCube()
263 m_AttributePosition.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
264 m_VertexArray.AddAttribute(&m_AttributePosition);
266 m_AttributeUV.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
267 m_VertexArray.AddAttribute(&m_AttributeUV);
269 // 6 sides of cube with 6 vertices.
270 m_VertexArray.SetNumberOfVertices(6 * 6);
271 m_VertexArray.Layout();
273 VertexArrayIterator<CVector3D> attrPosition = m_AttributePosition.GetIterator<CVector3D>();
274 VertexArrayIterator<CVector3D> attrUV = m_AttributeUV.GetIterator<CVector3D>();
276 #define ADD_VERTEX(U, V, W, VX, VY, VZ) \
277 STMT( \
278 attrPosition->X = VX; \
279 attrPosition->Y = VY; \
280 attrPosition->Z = VZ; \
281 ++attrPosition; \
282 attrUV->X = U; \
283 attrUV->Y = V; \
284 attrUV->Z = W; \
285 ++attrUV;)
287 // Axis -X
288 ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
289 ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
290 ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
291 ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
292 ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
293 ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
295 // Axis +X
296 ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
297 ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
298 ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
299 ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
300 ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
301 ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
303 // Axis -Y
304 ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
305 ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
306 ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
307 ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
308 ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
309 ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
311 // Axis +Y
312 ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
313 ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
314 ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
315 ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
316 ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
317 ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
319 // Axis -Z
320 ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
321 ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
322 ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
323 ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
324 ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
325 ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
327 // Axis +Z
328 ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
329 ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
330 ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
331 ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
332 ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
333 ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
334 #undef ADD_VERTEX
336 m_VertexArray.Upload();
337 m_VertexArray.FreeBackingStore();
339 const uint32_t stride = m_VertexArray.GetStride();
340 const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
341 {Renderer::Backend::VertexAttributeStream::POSITION,
342 m_AttributePosition.format, m_AttributePosition.offset, stride,
343 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
344 {Renderer::Backend::VertexAttributeStream::UV0,
345 m_AttributeUV.format, m_AttributeUV.offset, stride,
346 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
348 m_VertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);