Line data Source code
1 : /* Copyright (C) 2023 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 "renderer/SkyManager.h"
21 :
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 "ps/VideoMode.h"
36 : #include "renderer/backend/IDevice.h"
37 : #include "renderer/Renderer.h"
38 : #include "renderer/SceneRenderer.h"
39 : #include "renderer/RenderingOptions.h"
40 :
41 : #include <algorithm>
42 :
43 6 : SkyManager::SkyManager()
44 6 : : m_VertexArray(Renderer::Backend::IBuffer::Type::VERTEX, false)
45 : {
46 6 : CFG_GET_VAL("showsky", m_SkyVisible);
47 6 : }
48 :
49 0 : void SkyManager::LoadAndUploadSkyTexturesIfNeeded(
50 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
51 : {
52 0 : if (m_VertexArray.GetNumberOfVertices() == 0)
53 0 : CreateSkyCube();
54 :
55 0 : if (m_SkyTextureCube)
56 0 : return;
57 :
58 0 : m_SkyTextureCube = g_Renderer.GetTextureManager().GetBlackTextureCube();
59 :
60 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Load Sky Textures");
61 : static const CStrW images[NUMBER_OF_TEXTURES + 1] = {
62 : L"front",
63 : L"back",
64 : L"top",
65 : L"top",
66 : L"right",
67 : L"left"
68 0 : };
69 :
70 : /*for (size_t i = 0; i < ARRAY_SIZE(m_SkyTexture); ++i)
71 : {
72 : VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(s_imageNames[i])+L".dds");
73 :
74 : CTextureProperties textureProps(path);
75 : textureProps.SetWrap(GL_CLAMP_TO_EDGE);
76 : CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
77 : texture->Prefetch();
78 : m_SkyTexture[i] = texture;
79 : }*/
80 :
81 : ///////////////////////////////////////////////////////////////////////////
82 : // HACK: THE HORRIBLENESS HERE IS OVER 9000. The following code is a HUGE hack and will be removed completely
83 : // as soon as all the hardcoded GL_TEXTURE_2D references are corrected in the TextureManager/OGL/tex libs.
84 :
85 0 : Tex textures[NUMBER_OF_TEXTURES + 1];
86 :
87 0 : for (size_t i = 0; i < NUMBER_OF_TEXTURES + 1; ++i)
88 : {
89 0 : VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i]) + L".dds");
90 :
91 0 : std::shared_ptr<u8> file;
92 : size_t fileSize;
93 0 : if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
94 : {
95 0 : path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i]) + L".dds.cached.dds");
96 0 : if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
97 : {
98 0 : LOGERROR("Error creating sky cubemap '%s', can't load file: '%s'.", m_SkySet.ToUTF8().c_str(), path.string8().c_str());
99 0 : return;
100 : }
101 : }
102 :
103 0 : if (textures[i].decode(file, fileSize) != INFO::OK ||
104 0 : textures[i].transform_to((textures[i].m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) != INFO::OK)
105 : {
106 0 : LOGERROR("Error creating sky cubemap '%s', can't decode file: '%s'.", m_SkySet.ToUTF8().c_str(), path.string8().c_str());
107 0 : return;
108 : }
109 :
110 0 : if (!is_pow2(textures[i].m_Width) || !is_pow2(textures[i].m_Height))
111 : {
112 0 : LOGERROR("Error creating sky cubemap '%s', cube textures should have power of 2 sizes.", m_SkySet.ToUTF8().c_str());
113 0 : return;
114 : }
115 :
116 0 : if (textures[i].m_Width != textures[0].m_Width || textures[i].m_Height != textures[0].m_Height)
117 : {
118 0 : LOGERROR("Error creating sky cubemap '%s', cube textures have different sizes.", m_SkySet.ToUTF8().c_str());
119 0 : return;
120 : }
121 : }
122 :
123 : std::unique_ptr<Renderer::Backend::ITexture> skyCubeMap =
124 0 : g_VideoMode.GetBackendDevice()->CreateTexture("SkyCubeMap",
125 : Renderer::Backend::ITexture::Type::TEXTURE_CUBE,
126 : Renderer::Backend::ITexture::Usage::TRANSFER_DST |
127 : Renderer::Backend::ITexture::Usage::SAMPLED,
128 0 : Renderer::Backend::Format::R8G8B8A8_UNORM, textures[0].m_Width, textures[0].m_Height,
129 0 : Renderer::Backend::Sampler::MakeDefaultSampler(
130 : Renderer::Backend::Sampler::Filter::LINEAR,
131 0 : Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, 1);
132 :
133 0 : std::vector<u8> rotated;
134 0 : for (size_t i = 0; i < NUMBER_OF_TEXTURES + 1; ++i)
135 : {
136 0 : u8* data = textures[i].get_data();
137 :
138 : // We need to rotate the side if it's looking up or down.
139 : // TODO: maybe it should be done during texture conversion.
140 0 : if (i == 2 || i == 3)
141 : {
142 0 : rotated.resize(textures[i].m_DataSize);
143 :
144 0 : for (size_t y = 0; y < textures[i].m_Height; ++y)
145 : {
146 0 : for (size_t x = 0; x < textures[i].m_Width; ++x)
147 : {
148 0 : const size_t invX = y;
149 0 : const size_t invY = textures[i].m_Width - x - 1;
150 :
151 0 : rotated[(y * textures[i].m_Width + x) * 4 + 0] = data[(invY * textures[i].m_Width + invX) * 4 + 0];
152 0 : rotated[(y * textures[i].m_Width + x) * 4 + 1] = data[(invY * textures[i].m_Width + invX) * 4 + 1];
153 0 : rotated[(y * textures[i].m_Width + x) * 4 + 2] = data[(invY * textures[i].m_Width + invX) * 4 + 2];
154 0 : rotated[(y * textures[i].m_Width + x) * 4 + 3] = data[(invY * textures[i].m_Width + invX) * 4 + 3];
155 : }
156 : }
157 :
158 0 : deviceCommandContext->UploadTexture(
159 : skyCubeMap.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
160 0 : &rotated[0], textures[i].m_DataSize, 0, i);
161 : }
162 : else
163 : {
164 0 : deviceCommandContext->UploadTexture(
165 : skyCubeMap.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
166 0 : data, textures[i].m_DataSize, 0, i);
167 : }
168 : }
169 :
170 0 : m_SkyTextureCube = g_Renderer.GetTextureManager().WrapBackendTexture(std::move(skyCubeMap));
171 : ///////////////////////////////////////////////////////////////////////////
172 : }
173 :
174 0 : Renderer::Backend::ITexture* SkyManager::GetSkyCube()
175 : {
176 0 : return m_SkyTextureCube->GetBackendTexture();
177 : }
178 :
179 0 : void SkyManager::SetSkySet(const CStrW& newSet)
180 : {
181 0 : if (newSet == m_SkySet)
182 0 : return;
183 :
184 0 : m_SkyTextureCube.reset();
185 :
186 0 : m_SkySet = newSet;
187 : }
188 :
189 0 : std::vector<CStrW> SkyManager::GetSkySets() const
190 : {
191 0 : std::vector<CStrW> skies;
192 :
193 : // Find all subdirectories in art/textures/skies
194 :
195 0 : const VfsPath path(L"art/textures/skies/");
196 0 : DirectoryNames subdirectories;
197 0 : if (g_VFS->GetDirectoryEntries(path, 0, &subdirectories) != INFO::OK)
198 : {
199 0 : LOGERROR("Error opening directory '%s'", path.string8());
200 0 : return std::vector<CStrW>(1, GetSkySet()); // just return what we currently have
201 : }
202 :
203 0 : for(size_t i = 0; i < subdirectories.size(); i++)
204 0 : skies.push_back(subdirectories[i].string());
205 0 : sort(skies.begin(), skies.end());
206 :
207 0 : return skies;
208 : }
209 :
210 0 : void SkyManager::RenderSky(
211 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
212 : {
213 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render sky");
214 :
215 : const CTexturePtr& skyTextureCube =
216 0 : !m_SkyVisible || m_SkySet.empty() || !m_SkyTextureCube
217 0 : ? g_Renderer.GetTextureManager().GetBlackTextureCube()
218 0 : : m_SkyTextureCube;
219 :
220 0 : const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera();
221 :
222 : CShaderTechniquePtr skytech =
223 0 : g_Renderer.GetShaderManager().LoadEffect(str_sky_simple);
224 0 : deviceCommandContext->SetGraphicsPipelineState(
225 0 : skytech->GetGraphicsPipelineState());
226 0 : deviceCommandContext->BeginPass();
227 0 : Renderer::Backend::IShaderProgram* shader = skytech->GetShader();
228 0 : deviceCommandContext->SetTexture(
229 0 : shader->GetBindingSlot(str_baseTex), skyTextureCube->GetBackendTexture());
230 :
231 : // Translate so the sky center is at the camera space origin.
232 0 : CMatrix3D translate;
233 0 : translate.SetTranslation(camera.GetOrientation().GetTranslation());
234 :
235 : // Currently we have a hardcoded near plane in the projection matrix.
236 0 : CMatrix3D scale;
237 0 : scale.SetScaling(10.0f, 10.0f, 10.0f);
238 :
239 : // Rotate so that the "left" face, which contains the brightest part of
240 : // each skymap, is in the direction of the sun from our light
241 : // environment.
242 0 : CMatrix3D rotate;
243 0 : rotate.SetYRotation(M_PI + g_Renderer.GetSceneRenderer().GetLightEnv().GetRotation());
244 :
245 0 : const CMatrix3D transform = camera.GetViewProjection() * translate * rotate * scale;
246 0 : deviceCommandContext->SetUniform(
247 0 : shader->GetBindingSlot(str_transform), transform.AsFloatArray());
248 :
249 0 : const uint32_t stride = m_VertexArray.GetStride();
250 0 : const uint32_t firstVertexOffset = m_VertexArray.GetOffset() * stride;
251 :
252 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
253 :
254 0 : deviceCommandContext->SetVertexBuffer(
255 0 : 0, m_VertexArray.GetBuffer(), firstVertexOffset);
256 :
257 0 : deviceCommandContext->Draw(0, m_VertexArray.GetNumberOfVertices());
258 :
259 0 : deviceCommandContext->EndPass();
260 0 : }
261 :
262 0 : void SkyManager::CreateSkyCube()
263 : {
264 0 : m_AttributePosition.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
265 0 : m_VertexArray.AddAttribute(&m_AttributePosition);
266 :
267 0 : m_AttributeUV.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
268 0 : m_VertexArray.AddAttribute(&m_AttributeUV);
269 :
270 : // 6 sides of cube with 6 vertices.
271 0 : m_VertexArray.SetNumberOfVertices(6 * 6);
272 0 : m_VertexArray.Layout();
273 :
274 0 : VertexArrayIterator<CVector3D> attrPosition = m_AttributePosition.GetIterator<CVector3D>();
275 0 : VertexArrayIterator<CVector3D> attrUV = m_AttributeUV.GetIterator<CVector3D>();
276 :
277 : #define ADD_VERTEX(U, V, W, VX, VY, VZ) \
278 : STMT( \
279 : attrPosition->X = VX; \
280 : attrPosition->Y = VY; \
281 : attrPosition->Z = VZ; \
282 : ++attrPosition; \
283 : attrUV->X = U; \
284 : attrUV->Y = V; \
285 : attrUV->Z = W; \
286 : ++attrUV;)
287 :
288 : // Axis -X
289 0 : ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
290 0 : ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
291 0 : ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
292 0 : ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
293 0 : ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
294 0 : ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
295 :
296 : // Axis +X
297 0 : ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
298 0 : ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
299 0 : ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
300 0 : ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
301 0 : ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
302 0 : ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
303 :
304 : // Axis -Y
305 0 : ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
306 0 : ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
307 0 : ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
308 0 : ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
309 0 : ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
310 0 : ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
311 :
312 : // Axis +Y
313 0 : ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
314 0 : ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
315 0 : ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
316 0 : ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
317 0 : ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
318 0 : ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
319 :
320 : // Axis -Z
321 0 : ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
322 0 : ADD_VERTEX(+1, +1, +1, -1.0f, -1.0f, -1.0f);
323 0 : ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
324 0 : ADD_VERTEX(-1, +1, +1, +1.0f, -1.0f, -1.0f);
325 0 : ADD_VERTEX(+1, -1, +1, -1.0f, +1.0f, -1.0f);
326 0 : ADD_VERTEX(-1, -1, +1, +1.0f, +1.0f, -1.0f);
327 :
328 : // Axis +Z
329 0 : ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
330 0 : ADD_VERTEX(-1, +1, -1, +1.0f, -1.0f, +1.0f);
331 0 : ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
332 0 : ADD_VERTEX(+1, +1, -1, -1.0f, -1.0f, +1.0f);
333 0 : ADD_VERTEX(-1, -1, -1, +1.0f, +1.0f, +1.0f);
334 0 : ADD_VERTEX(+1, -1, -1, -1.0f, +1.0f, +1.0f);
335 : #undef ADD_VERTEX
336 :
337 0 : m_VertexArray.Upload();
338 0 : m_VertexArray.FreeBackingStore();
339 :
340 0 : const uint32_t stride = m_VertexArray.GetStride();
341 0 : const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
342 : {Renderer::Backend::VertexAttributeStream::POSITION,
343 0 : m_AttributePosition.format, m_AttributePosition.offset, stride,
344 : Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
345 : {Renderer::Backend::VertexAttributeStream::UV0,
346 0 : m_AttributeUV.format, m_AttributeUV.offset, stride,
347 : Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
348 0 : }};
349 0 : m_VertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);
350 3 : }
|