SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX11CubemapTexture.cpp
Go to the documentation of this file.
2
3#ifdef PLATFORM_WIN
4
5#include <Core/Logger.hpp>
6#include <stb_image.h>
7#include <cstring>
8#include <cmath>
9#include <algorithm>
10#include <vector>
11
12namespace Sleak {
13namespace RenderEngine {
14
16 : m_device(device) {
17 device->GetImmediateContext(m_deviceContext.GetAddressOf());
18}
19
20DirectX11CubemapTexture::~DirectX11CubemapTexture() {
21 m_shaderResourceView.Reset();
22 m_texture.Reset();
23 m_samplerState.Reset();
24}
25
26bool DirectX11CubemapTexture::LoadCubemap(
27 const std::array<std::string, 6>& facePaths) {
28 stbi_set_flip_vertically_on_load(false);
29
30 struct FaceData {
31 unsigned char* pixels = nullptr;
32 int w = 0, h = 0;
33 };
34 std::array<FaceData, 6> faces;
35
36 for (int i = 0; i < 6; i++) {
37 int channels;
38 faces[i].pixels = stbi_load(facePaths[i].c_str(), &faces[i].w,
39 &faces[i].h, &channels, 4);
40 if (!faces[i].pixels) {
41 SLEAK_ERROR("DirectX11CubemapTexture: Failed to load face {}: {}",
42 i, facePaths[i]);
43 for (int j = 0; j < i; j++)
44 stbi_image_free(faces[j].pixels);
45 return false;
46 }
47 }
48
49 // All faces must be same size
50 for (int i = 1; i < 6; i++) {
51 if (faces[i].w != faces[0].w || faces[i].h != faces[0].h) {
53 "DirectX11CubemapTexture: Face {} size ({}x{}) doesn't match "
54 "face 0 ({}x{})",
55 i, faces[i].w, faces[i].h, faces[0].w, faces[0].h);
56 for (int j = 0; j < 6; j++)
57 stbi_image_free(faces[j].pixels);
58 return false;
59 }
60 }
61
62 uint32_t faceSize = static_cast<uint32_t>(faces[0].w);
63 std::vector<unsigned char*> facePointers;
64 for (int i = 0; i < 6; i++)
65 facePointers.push_back(faces[i].pixels);
66
67 bool result = CreateCubemapFromFaces(facePointers, faceSize);
68
69 for (int i = 0; i < 6; i++)
70 stbi_image_free(faces[i].pixels);
71
72 if (result) {
73 SLEAK_INFO("DirectX11CubemapTexture: Loaded cubemap ({}x{}, 6 faces)",
74 faceSize, faceSize);
75 }
76 return result;
77}
78
79/// Resamples an equirectangular panorama into 6 cube faces via per-pixel direction lookup.
80bool DirectX11CubemapTexture::LoadEquirectangular(const std::string& path,
81 uint32_t faceSize) {
82 stbi_set_flip_vertically_on_load(false);
83
84 int panW, panH, channels;
85 unsigned char* panorama =
86 stbi_load(path.c_str(), &panW, &panH, &channels, 4);
87 if (!panorama) {
88 SLEAK_ERROR("DirectX11CubemapTexture: Failed to load panorama: {}",
89 path);
90 return false;
91 }
92
93 size_t faceBytes = static_cast<size_t>(faceSize) * faceSize * 4;
94 std::vector<std::vector<unsigned char>> allFaces(6);
95 for (int i = 0; i < 6; i++)
96 allFaces[i].resize(faceBytes);
97
98 for (int face = 0; face < 6; ++face) {
99 unsigned char* faceData = allFaces[face].data();
100
101 for (uint32_t y = 0; y < faceSize; ++y) {
102 for (uint32_t x = 0; x < faceSize; ++x) {
103 float u = (2.0f * (x + 0.5f) / faceSize) - 1.0f;
104 float v = (2.0f * (y + 0.5f) / faceSize) - 1.0f;
105
106 float dx, dy, dz;
107 switch (face) {
108 case 0: dx = 1.0f; dy = -v; dz = -u; break; // +X
109 case 1: dx = -1.0f; dy = -v; dz = u; break; // -X
110 case 2: dx = u; dy = 1.0f; dz = v; break; // +Y
111 case 3: dx = u; dy = -1.0f; dz = -v; break; // -Y
112 case 4: dx = u; dy = -v; dz = 1.0f; break; // +Z
113 case 5: dx = -u; dy = -v; dz = -1.0f; break; // -Z
114 default: dx = dy = dz = 0.0f; break;
115 }
116
117 float len = std::sqrt(dx * dx + dy * dy + dz * dz);
118 dx /= len; dy /= len; dz /= len;
119
120 float lon = std::atan2(dz, dx);
121 float lat = std::asin(std::clamp(dy, -1.0f, 1.0f));
122
123 float panU = 0.5f + lon / (2.0f * 3.14159265f);
124 float panV = 0.5f - lat / 3.14159265f;
125
126 float srcX = panU * (panW - 1);
127 float srcY = panV * (panH - 1);
128 int x0 = static_cast<int>(srcX);
129 int y0 = static_cast<int>(srcY);
130 int x1 = std::min(x0 + 1, panW - 1);
131 int y1 = std::min(y0 + 1, panH - 1);
132 x0 = std::clamp(x0, 0, panW - 1);
133 y0 = std::clamp(y0, 0, panH - 1);
134 float fx = srcX - x0;
135 float fy = srcY - y0;
136
137 size_t idx = (y * faceSize + x) * 4;
138 for (int c = 0; c < 4; ++c) {
139 float c00 = panorama[(y0 * panW + x0) * 4 + c];
140 float c10 = panorama[(y0 * panW + x1) * 4 + c];
141 float c01 = panorama[(y1 * panW + x0) * 4 + c];
142 float c11 = panorama[(y1 * panW + x1) * 4 + c];
143 float val = c00 * (1 - fx) * (1 - fy) +
144 c10 * fx * (1 - fy) +
145 c01 * (1 - fx) * fy +
146 c11 * fx * fy;
147 faceData[idx + c] = static_cast<unsigned char>(
148 std::clamp(val, 0.0f, 255.0f));
149 }
150 }
151 }
152 }
153
154 stbi_image_free(panorama);
155
156 std::vector<unsigned char*> facePointers;
157 for (int i = 0; i < 6; i++)
158 facePointers.push_back(allFaces[i].data());
159
160 bool result = CreateCubemapFromFaces(facePointers, faceSize);
161
162 if (result) {
164 "DirectX11CubemapTexture: Loaded equirectangular panorama "
165 "({}x{} face)",
166 faceSize, faceSize);
167 }
168 return result;
169}
170
171bool DirectX11CubemapTexture::CreateCubemapFromFaces(
172 const std::vector<unsigned char*>& faceData, uint32_t faceSize) {
173 m_width = faceSize;
174 m_height = faceSize;
175
176 // Create texture description for cubemap
177 D3D11_TEXTURE2D_DESC texDesc = {};
178 texDesc.Width = faceSize;
179 texDesc.Height = faceSize;
180 texDesc.MipLevels = 1;
181 texDesc.ArraySize = 6;
182 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
183 texDesc.SampleDesc.Count = 1;
184 texDesc.SampleDesc.Quality = 0;
185 texDesc.Usage = D3D11_USAGE_DEFAULT;
186 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
187 texDesc.CPUAccessFlags = 0;
188 texDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
189
190 // Prepare subresource data for all 6 faces
191 D3D11_SUBRESOURCE_DATA subData[6] = {};
192 uint32_t rowPitch = faceSize * 4;
193 for (int i = 0; i < 6; i++) {
194 subData[i].pSysMem = faceData[i];
195 subData[i].SysMemPitch = rowPitch;
196 subData[i].SysMemSlicePitch = 0;
197 }
198
199 HRESULT hr =
200 m_device->CreateTexture2D(&texDesc, subData, m_texture.GetAddressOf());
201 if (FAILED(hr)) {
203 "DirectX11CubemapTexture: Failed to create cubemap texture! "
204 "HRESULT: 0x{:08X}",
205 static_cast<unsigned int>(hr));
206 return false;
207 }
208
209 // Create SRV for cubemap
210 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
211 srvDesc.Format = texDesc.Format;
212 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
213 srvDesc.TextureCube.MipLevels = 1;
214 srvDesc.TextureCube.MostDetailedMip = 0;
215
216 hr = m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc,
217 m_shaderResourceView.GetAddressOf());
218 if (FAILED(hr)) {
220 "DirectX11CubemapTexture: Failed to create SRV! HRESULT: "
221 "0x{:08X}",
222 static_cast<unsigned int>(hr));
223 return false;
224 }
225
226 CreateSamplerState();
227 return true;
228}
229
230void DirectX11CubemapTexture::CreateSamplerState() {
231 D3D11_SAMPLER_DESC samplerDesc = {};
232 samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
233 samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
234 samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
235 samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
236 samplerDesc.MipLODBias = 0.0f;
237 samplerDesc.MaxAnisotropy = 1;
238 samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
239 samplerDesc.MinLOD = 0;
240 samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
241
242 HRESULT hr =
243 m_device->CreateSamplerState(&samplerDesc, m_samplerState.GetAddressOf());
244 if (FAILED(hr)) {
246 "DirectX11CubemapTexture: Failed to create sampler state! "
247 "HRESULT: 0x{:08X}",
248 static_cast<unsigned int>(hr));
249 }
250}
251
252bool DirectX11CubemapTexture::LoadFromMemory(const void* data, uint32_t width,
253 uint32_t height,
254 TextureFormat format) {
255 (void)data;
256 (void)width;
257 (void)height;
258 (void)format;
259 return false;
260}
261
262bool DirectX11CubemapTexture::LoadFromFile(const std::string& filePath) {
263 (void)filePath;
264 return false;
265}
266
267void DirectX11CubemapTexture::Bind(uint32_t slot) const {
268 if (m_shaderResourceView && m_samplerState) {
269 m_deviceContext->PSSetShaderResources(
270 slot, 1, m_shaderResourceView.GetAddressOf());
271 m_deviceContext->PSSetSamplers(slot, 1, m_samplerState.GetAddressOf());
272 }
273}
274
275void DirectX11CubemapTexture::Unbind() const {
276 ID3D11ShaderResourceView* nullSRV = nullptr;
277 ID3D11SamplerState* nullSampler = nullptr;
278 m_deviceContext->PSSetShaderResources(0, 1, &nullSRV);
279 m_deviceContext->PSSetSamplers(0, 1, &nullSampler);
280}
281
282void DirectX11CubemapTexture::SetFilter(TextureFilter filter) {
283 (void)filter;
284}
285
286void DirectX11CubemapTexture::SetWrapMode(TextureWrapMode wrapMode) {
287 (void)wrapMode;
288}
289
290} // namespace RenderEngine
291} // namespace Sleak
292
293#endif // PLATFORM_WIN
int width
int height
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
#define SLEAK_INFO(...)
Definition Logger.hpp:20
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10