SleakEngine 0.1.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX12CubemapTexture.cpp
Go to the documentation of this file.
2
3#ifdef PLATFORM_WIN
4
5#include <stb_image.h>
6
7#include <Core/Logger.hpp>
10#include <algorithm>
11#include <cmath>
12#include <cstring>
13#include <utility>
14#include <vector>
15
16namespace Sleak {
17namespace RenderEngine {
18
19DirectX12CubemapTexture::DirectX12CubemapTexture(
20 ID3D12Device* device, ID3D12CommandQueue* commandQueue,
21 DirectX12UploadContext* uploader)
22 : m_device(device), m_commandQueue(commandQueue), m_uploader(uploader) {}
23
24DirectX12CubemapTexture::~DirectX12CubemapTexture() {
25 DirectX12Buffer::DeferCleanup(std::move(m_srvHeap));
26 DirectX12Buffer::DeferCleanup(std::move(m_texture));
27}
28
29bool DirectX12CubemapTexture::LoadCubemap(
30 const std::array<std::string, 6>& facePaths) {
31 stbi_set_flip_vertically_on_load(false);
32
33 struct FaceData {
34 unsigned char* pixels = nullptr;
35 int w = 0, h = 0;
36 };
37 std::array<FaceData, 6> faces;
38
39 for (int i = 0; i < 6; i++) {
40 int channels;
41 faces[i].pixels = stbi_load(facePaths[i].c_str(), &faces[i].w,
42 &faces[i].h, &channels, 4);
43 if (!faces[i].pixels) {
45 "DirectX12CubemapTexture: Failed to load face {}: {}", i,
46 facePaths[i]);
47 for (int j = 0; j < i; j++)
48 stbi_image_free(faces[j].pixels);
49 return false;
50 }
51 }
52
53 for (int i = 1; i < 6; i++) {
54 if (faces[i].w != faces[0].w || faces[i].h != faces[0].h) {
56 "DirectX12CubemapTexture: Face {} size ({}x{}) doesn't match "
57 "face 0 ({}x{})",
58 i, faces[i].w, faces[i].h, faces[0].w, faces[0].h);
59 for (int j = 0; j < 6; j++)
60 stbi_image_free(faces[j].pixels);
61 return false;
62 }
63 }
64
65 uint32_t faceSize = static_cast<uint32_t>(faces[0].w);
66 std::vector<unsigned char*> facePointers;
67 for (int i = 0; i < 6; i++)
68 facePointers.push_back(faces[i].pixels);
69
70 bool result = CreateCubemapFromFaces(facePointers, faceSize);
71
72 for (int i = 0; i < 6; i++)
73 stbi_image_free(faces[i].pixels);
74
75 if (result) {
77 "DirectX12CubemapTexture: Loaded cubemap ({}x{}, 6 faces)",
78 faceSize, faceSize);
79 }
80 return result;
81}
82
83bool DirectX12CubemapTexture::LoadEquirectangular(const std::string& path,
84 uint32_t faceSize) {
85 stbi_set_flip_vertically_on_load(false);
86
87 int panW, panH, channels;
88 unsigned char* panorama =
89 stbi_load(path.c_str(), &panW, &panH, &channels, 4);
90 if (!panorama) {
92 "DirectX12CubemapTexture: Failed to load panorama: {}", path);
93 return false;
94 }
95
96 size_t faceBytes = static_cast<size_t>(faceSize) * faceSize * 4;
97 std::vector<std::vector<unsigned char>> allFaces(6);
98 for (int i = 0; i < 6; i++)
99 allFaces[i].resize(faceBytes);
100
101 for (int face = 0; face < 6; ++face) {
102 unsigned char* faceData = allFaces[face].data();
103
104 for (uint32_t y = 0; y < faceSize; ++y) {
105 for (uint32_t x = 0; x < faceSize; ++x) {
106 float u = (2.0f * (x + 0.5f) / faceSize) - 1.0f;
107 float v = (2.0f * (y + 0.5f) / faceSize) - 1.0f;
108
109 float dx, dy, dz;
110 switch (face) {
111 case 0: dx = 1.0f; dy = -v; dz = -u; break; // +X
112 case 1: dx = -1.0f; dy = -v; dz = u; break; // -X
113 case 2: dx = u; dy = 1.0f; dz = v; break; // +Y
114 case 3: dx = u; dy = -1.0f; dz = -v; break; // -Y
115 case 4: dx = u; dy = -v; dz = 1.0f; break; // +Z
116 case 5: dx = -u; dy = -v; dz = -1.0f; break; // -Z
117 default: dx = dy = dz = 0.0f; break;
118 }
119
120 float len = std::sqrt(dx * dx + dy * dy + dz * dz);
121 dx /= len; dy /= len; dz /= len;
122
123 float lon = std::atan2(dz, dx);
124 float lat = std::asin(std::clamp(dy, -1.0f, 1.0f));
125
126 float panU = 0.5f + lon / (2.0f * 3.14159265f);
127 float panV = 0.5f - lat / 3.14159265f;
128
129 float srcX = panU * (panW - 1);
130 float srcY = panV * (panH - 1);
131 int x0 = static_cast<int>(srcX);
132 int y0 = static_cast<int>(srcY);
133 int x1 = std::min(x0 + 1, panW - 1);
134 int y1 = std::min(y0 + 1, panH - 1);
135 x0 = std::clamp(x0, 0, panW - 1);
136 y0 = std::clamp(y0, 0, panH - 1);
137 float fx = srcX - x0;
138 float fy = srcY - y0;
139
140 size_t idx = (y * faceSize + x) * 4;
141 for (int c = 0; c < 4; ++c) {
142 float c00 = panorama[(y0 * panW + x0) * 4 + c];
143 float c10 = panorama[(y0 * panW + x1) * 4 + c];
144 float c01 = panorama[(y1 * panW + x0) * 4 + c];
145 float c11 = panorama[(y1 * panW + x1) * 4 + c];
146 float val = c00 * (1 - fx) * (1 - fy) +
147 c10 * fx * (1 - fy) +
148 c01 * (1 - fx) * fy +
149 c11 * fx * fy;
150 faceData[idx + c] = static_cast<unsigned char>(
151 std::clamp(val, 0.0f, 255.0f));
152 }
153 }
154 }
155 }
156
157 stbi_image_free(panorama);
158
159 std::vector<unsigned char*> facePointers;
160 for (int i = 0; i < 6; i++)
161 facePointers.push_back(allFaces[i].data());
162
163 bool result = CreateCubemapFromFaces(facePointers, faceSize);
164
165 if (result) {
167 "DirectX12CubemapTexture: Loaded equirectangular panorama "
168 "({}x{} face)",
169 faceSize, faceSize);
170 }
171 return result;
172}
173
174bool DirectX12CubemapTexture::CreateCubemapFromFaces(
175 const std::vector<unsigned char*>& faceData, uint32_t faceSize) {
176 if (!m_device || faceData.size() != 6) return false;
177
178 m_width = faceSize;
179 m_height = faceSize;
180
181 // Create cubemap texture resource (6 array slices)
182 D3D12_RESOURCE_DESC texDesc = {};
183 texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
184 texDesc.Alignment = 0;
185 texDesc.Width = faceSize;
186 texDesc.Height = faceSize;
187 texDesc.DepthOrArraySize = 6;
188 texDesc.MipLevels = 1;
189 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
190 texDesc.SampleDesc.Count = 1;
191 texDesc.SampleDesc.Quality = 0;
192 texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
193 texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
194
195 D3D12_HEAP_PROPERTIES heapProps = {};
196 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
197 heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
198 heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
199 heapProps.CreationNodeMask = 1;
200 heapProps.VisibleNodeMask = 1;
201
202 HRESULT hr = m_device->CreateCommittedResource(
203 &heapProps, D3D12_HEAP_FLAG_NONE, &texDesc,
204 D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
205 IID_PPV_ARGS(&m_texture));
206
207 if (FAILED(hr)) {
209 "DirectX12CubemapTexture: Failed to create texture resource "
210 "HRESULT: 0x{:08X}",
211 static_cast<unsigned int>(hr));
212 return false;
213 }
214
215 // Get copyable footprints for all 6 subresources
216 D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprints[6];
217 UINT numRows[6];
218 UINT64 rowSizeInBytes[6];
219 UINT64 totalBytes;
220 m_device->GetCopyableFootprints(&texDesc, 0, 6, 0, footprints, numRows,
221 rowSizeInBytes, &totalBytes);
222
223 DirectX12UploadContext local;
224 DirectX12UploadContext* uploader = m_uploader;
225 if (!uploader) {
226 if (!local.Initialize(m_device, m_commandQueue)) return false;
227 uploader = &local;
228 }
229
230 ID3D12Resource* staging = nullptr;
231 BYTE* mapped =
232 static_cast<BYTE*>(uploader->AllocateStaging(totalBytes, &staging));
233 if (!mapped) {
235 "DirectX12CubemapTexture: Failed to allocate staging memory");
236 return false;
237 }
238
239 uint32_t srcRowPitch = faceSize * 4;
240 for (int face = 0; face < 6; face++) {
241 BYTE* destBase = mapped + footprints[face].Offset;
242 const BYTE* srcData = reinterpret_cast<const BYTE*>(faceData[face]);
243
244 for (UINT row = 0; row < numRows[face]; row++) {
245 memcpy(destBase + row * footprints[face].Footprint.RowPitch,
246 srcData + row * srcRowPitch, srcRowPitch);
247 }
248 }
249
250 ID3D12GraphicsCommandList* cmdList = uploader->GetCommandList();
251
252 // Copy each face from upload buffer to texture
253 for (UINT face = 0; face < 6; face++) {
254 D3D12_TEXTURE_COPY_LOCATION dst = {};
255 dst.pResource = m_texture.Get();
256 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
257 dst.SubresourceIndex = face;
258
259 D3D12_TEXTURE_COPY_LOCATION src = {};
260 src.pResource = staging;
261 src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
262 src.PlacedFootprint = footprints[face];
263
264 cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
265 }
266
267 // Transition to PIXEL_SHADER_RESOURCE
268 D3D12_RESOURCE_BARRIER barrier = {};
269 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
270 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
271 barrier.Transition.pResource = m_texture.Get();
272 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
273 barrier.Transition.StateAfter =
274 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
275 barrier.Transition.Subresource =
276 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
277 cmdList->ResourceBarrier(1, &barrier);
278
279 if (uploader == &local) local.Flush();
280
281 // Create SRV descriptor heap
282 D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
283 srvHeapDesc.NumDescriptors = 1;
284 srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
285 srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
286
287 hr = m_device->CreateDescriptorHeap(&srvHeapDesc,
288 IID_PPV_ARGS(&m_srvHeap));
289 if (FAILED(hr)) {
291 "DirectX12CubemapTexture: Failed to create SRV descriptor heap");
292 return false;
293 }
294
295 // Create SRV for TextureCube
296 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
297 srvDesc.Shader4ComponentMapping =
298 D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
299 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
300 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
301 srvDesc.TextureCube.MipLevels = 1;
302 srvDesc.TextureCube.MostDetailedMip = 0;
303 srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
304
305 m_device->CreateShaderResourceView(
306 m_texture.Get(), &srvDesc,
307 m_srvHeap->GetCPUDescriptorHandleForHeapStart());
308
309 return true;
310}
311
312bool DirectX12CubemapTexture::LoadFromMemory(const void* data, uint32_t width,
313 uint32_t height,
314 TextureFormat format) {
315 (void)data;
316 (void)width;
317 (void)height;
318 (void)format;
319 return false;
320}
321
322bool DirectX12CubemapTexture::LoadFromFile(const std::string& filePath) {
323 (void)filePath;
324 return false;
325}
326
327void DirectX12CubemapTexture::Bind(uint32_t slot) const {
328 (void)slot;
329 // Binding in DX12 requires a command list — use BindToCommandList instead
330}
331
332void DirectX12CubemapTexture::Unbind() const {
333 // No-op in DX12
334}
335
336void DirectX12CubemapTexture::SetFilter(TextureFilter filter) {
337 (void)filter;
338}
339
340void DirectX12CubemapTexture::SetWrapMode(TextureWrapMode wrapMode) {
341 (void)wrapMode;
342}
343
344void DirectX12CubemapTexture::BindToCommandList(
345 ID3D12GraphicsCommandList* cmdList, UINT rootParameterIndex) const {
346 if (!cmdList) return;
347
348 if (m_usesSharedHeap) {
349 // Fast path: shared heap already bound by BeginRender
350 cmdList->SetGraphicsRootDescriptorTable(rootParameterIndex, m_srvGpuHandle);
351 } else if (m_srvHeap) {
352 // Legacy fallback
353 ID3D12DescriptorHeap* heaps[] = {m_srvHeap.Get()};
354 cmdList->SetDescriptorHeaps(1, heaps);
355 cmdList->SetGraphicsRootDescriptorTable(
356 rootParameterIndex,
357 m_srvHeap->GetGPUDescriptorHandleForHeapStart());
358 }
359}
360
361void DirectX12CubemapTexture::CreateSRVIntoHandle(
362 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle) {
363 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
364 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
365 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
366 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
367 srvDesc.TextureCube.MipLevels = 1;
368 srvDesc.TextureCube.MostDetailedMip = 0;
369 srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
370 m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, cpuHandle);
371}
372
373} // namespace RenderEngine
374} // namespace Sleak
375
376#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