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"
27 #include "lib/tex/tex.h"
28 #include "maths/MathUtil.h"
29 #include "ps/CLogger.h"
30 #include "ps/ConfigDB.h"
32 #include "ps/CStrInternStatic.h"
33 #include "ps/Filesystem.h"
35 #include "renderer/backend/IDevice.h"
36 #include "renderer/Renderer.h"
37 #include "renderer/SceneRenderer.h"
38 #include "renderer/RenderingOptions.h"
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)
57 m_SkyTextureCube
= g_Renderer
.GetTextureManager().GetBlackTextureCube();
59 GPU_SCOPED_LABEL(deviceCommandContext
, "Load Sky Textures");
60 static const CStrW images
[NUMBER_OF_TEXTURES
+ 1] = {
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);
77 m_SkyTexture[i] = texture;
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
;
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());
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());
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());
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());
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
);
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
)
183 m_SkyTextureCube
.reset();
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());
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()
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.
232 translate
.SetTranslation(camera
.GetOrientation().GetTranslation());
234 // Currently we have a hardcoded near plane in the projection matrix.
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
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) \
278 attrPosition->X = VX; \
279 attrPosition->Y = VY; \
280 attrPosition->Z = VZ; \
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
);
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
);
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
);
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
);
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
);
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
);
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
);