Wildvine Engine
Referencia Doxygen del codigo propio de Wildvine Engine.
Cargando...
Buscando...
Nada coincide
Texture.cpp
Ir a la documentación de este archivo.
1
6#define STB_IMAGE_IMPLEMENTATION
7#include "stb_image.h"
8#include "Texture.h"
9#include "Device.h"
10#include "DeviceContext.h"
11#include <cstdint>
12#include <fstream>
13
14namespace {
15constexpr uint32_t kTextureCacheMagic = 0x58545657; // WVTX
16constexpr uint32_t kTextureCacheVersion = 1;
17
19 int width = 0;
20 int height = 0;
21 std::vector<unsigned char> rgba;
22};
23
24bool GetFileWriteTime(const std::string& path, ULONGLONG& outWriteTime) {
25 WIN32_FILE_ATTRIBUTE_DATA attributes{};
26 if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attributes)) {
27 return false;
28 }
29
30 ULARGE_INTEGER fileTime{};
31 fileTime.LowPart = attributes.ftLastWriteTime.dwLowDateTime;
32 fileTime.HighPart = attributes.ftLastWriteTime.dwHighDateTime;
33 outWriteTime = fileTime.QuadPart;
34 return true;
35}
36
37std::string GetTextureCachePath(const std::string& sourcePath) {
38 return sourcePath + ".wvtx";
39}
40
41bool IsTextureCacheUpToDate(const std::string& sourcePath, const std::string& cachePath) {
42 ULONGLONG sourceWriteTime = 0;
43 ULONGLONG cacheWriteTime = 0;
44 if (!GetFileWriteTime(sourcePath, sourceWriteTime)) {
45 return false;
46 }
47 if (!GetFileWriteTime(cachePath, cacheWriteTime)) {
48 return false;
49 }
50 return cacheWriteTime >= sourceWriteTime;
51}
52
53bool SaveTextureCache(const std::string& cachePath, int width, int height, const unsigned char* data) {
54 std::ofstream stream(cachePath, std::ios::binary | std::ios::trunc);
55 if (!stream.is_open()) {
56 return false;
57 }
58
59 const uint32_t dataSize = static_cast<uint32_t>(width * height * 4);
60 stream.write(reinterpret_cast<const char*>(&kTextureCacheMagic), sizeof(kTextureCacheMagic));
61 stream.write(reinterpret_cast<const char*>(&kTextureCacheVersion), sizeof(kTextureCacheVersion));
62 stream.write(reinterpret_cast<const char*>(&width), sizeof(width));
63 stream.write(reinterpret_cast<const char*>(&height), sizeof(height));
64 stream.write(reinterpret_cast<const char*>(&dataSize), sizeof(dataSize));
65 stream.write(reinterpret_cast<const char*>(data), dataSize);
66 return stream.good();
67}
68
69bool LoadTextureCache(const std::string& cachePath, CachedTextureData& outTexture) {
70 std::ifstream stream(cachePath, std::ios::binary);
71 if (!stream.is_open()) {
72 return false;
73 }
74
75 uint32_t magic = 0;
76 uint32_t version = 0;
77 uint32_t dataSize = 0;
78 stream.read(reinterpret_cast<char*>(&magic), sizeof(magic));
79 stream.read(reinterpret_cast<char*>(&version), sizeof(version));
80 stream.read(reinterpret_cast<char*>(&outTexture.width), sizeof(outTexture.width));
81 stream.read(reinterpret_cast<char*>(&outTexture.height), sizeof(outTexture.height));
82 stream.read(reinterpret_cast<char*>(&dataSize), sizeof(dataSize));
83
84 if (!stream.good() ||
85 magic != kTextureCacheMagic ||
86 version != kTextureCacheVersion ||
87 outTexture.width <= 0 ||
88 outTexture.height <= 0 ||
89 dataSize != static_cast<uint32_t>(outTexture.width * outTexture.height * 4)) {
90 return false;
91 }
92
93 outTexture.rgba.resize(dataSize);
94 stream.read(reinterpret_cast<char*>(outTexture.rgba.data()), dataSize);
95 return stream.good();
96}
97
99 int width,
100 int height,
101 const unsigned char* data,
102 ID3D11Texture2D** outTexture,
103 ID3D11ShaderResourceView** outSRV) {
104 D3D11_TEXTURE2D_DESC textureDesc = {};
105 textureDesc.Width = width;
106 textureDesc.Height = height;
107 textureDesc.MipLevels = 1;
108 textureDesc.ArraySize = 1;
109 textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
110 textureDesc.SampleDesc.Count = 1;
111 textureDesc.Usage = D3D11_USAGE_DEFAULT;
112 textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
113
114 D3D11_SUBRESOURCE_DATA initData = {};
115 initData.pSysMem = data;
116 initData.SysMemPitch = width * 4;
117
118 HRESULT hr = device.CreateTexture2D(&textureDesc, &initData, outTexture);
119 if (FAILED(hr)) {
120 return hr;
121 }
122
123 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
124 srvDesc.Format = textureDesc.Format;
125 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
126 srvDesc.Texture2D.MipLevels = 1;
127
128 hr = device.m_device->CreateShaderResourceView(*outTexture, &srvDesc, outSRV);
129 if (FAILED(hr)) {
130 if (*outTexture != nullptr) {
131 (*outTexture)->Release();
132 *outTexture = nullptr;
133 }
134 }
135 return hr;
136}
137
138HRESULT InitTextureFromImage(Device& device, const std::string& fullPath, Texture& texture) {
139 CachedTextureData cachedTexture;
140 const std::string cachePath = GetTextureCachePath(fullPath);
141
142 int width = 0;
143 int height = 0;
144 int channels = 0;
145 unsigned char* decodedData = nullptr;
146 const unsigned char* uploadData = nullptr;
147
148 if (IsTextureCacheUpToDate(fullPath, cachePath) && LoadTextureCache(cachePath, cachedTexture)) {
149 width = cachedTexture.width;
150 height = cachedTexture.height;
151 uploadData = cachedTexture.rgba.data();
152 }
153 else {
154 decodedData = stbi_load(fullPath.c_str(), &width, &height, &channels, 4);
155 if (!decodedData) {
156 ERROR("Texture", "init",
157 ("Failed to load texture: " + std::string(stbi_failure_reason())).c_str());
158 return E_FAIL;
159 }
160 uploadData = decodedData;
161 SaveTextureCache(cachePath, width, height, decodedData);
162 }
163
164 HRESULT hr = CreateTextureFromRGBA(device, width, height, uploadData, &texture.m_texture, &texture.m_textureFromImg);
165 if (decodedData) {
166 stbi_image_free(decodedData);
167 }
168
169 if (FAILED(hr)) {
170 SAFE_RELEASE(texture.m_texture);
172 ERROR("Texture", "init", "Failed to create shader resource view for cached image texture");
173 return hr;
174 }
175
176 SAFE_RELEASE(texture.m_texture);
177 return S_OK;
178}
179}
180
181HRESULT
183 const std::string& textureName,
184 ExtensionType extensionType) {
185 if (!device.m_device) {
186 ERROR("Texture", "init", "Device is null.");
187 return E_POINTER;
188 }
189 if (textureName.empty()) {
190 ERROR("Texture", "init", "Texture name cannot be empty.");
191 return E_INVALIDARG;
192 }
193
194 HRESULT hr = S_OK;
195
196 switch (extensionType) {
197 case DDS: {
198 m_textureName = textureName + ".dds";
199
200 hr = D3DX11CreateShaderResourceViewFromFile(
201 device.m_device,
202 m_textureName.c_str(),
203 nullptr,
204 nullptr,
206 nullptr
207 );
208
209 if (FAILED(hr)) {
210 ERROR("Texture", "init",
211 ("Failed to load DDS texture. Verify filepath: " + m_textureName).c_str());
212 return hr;
213 }
214 break;
215 }
216
217 case PNG: {
218 m_textureName = textureName + ".png";
219 hr = InitTextureFromImage(device, m_textureName, *this);
220 break;
221 }
222 case JPG: {
223 m_textureName = textureName + ".jpg";
224 hr = InitTextureFromImage(device, m_textureName, *this);
225 break;
226 }
227 default:
228 ERROR("Texture", "init", "Unsupported extension type");
229 return E_INVALIDARG;
230 }
231
232 return hr;
233}
234
235HRESULT
237 unsigned int width,
238 unsigned int height,
239 DXGI_FORMAT Format,
240 unsigned int BindFlags,
241 unsigned int sampleCount,
242 unsigned int qualityLevels) {
243 if (!device.m_device) {
244 ERROR("Texture", "init", "Device is null.");
245 return E_POINTER;
246 }
247 if (width == 0 || height == 0) {
248 ERROR("Texture", "init", "Width and height must be greater than 0");
249 return E_INVALIDARG;
250 }
251
252 D3D11_TEXTURE2D_DESC desc;
253 memset(&desc, 0, sizeof(desc));
254 desc.Width = width;
255 desc.Height = height;
256 desc.MipLevels = 1;
257 desc.ArraySize = 1;
258 desc.Format = Format;
259 desc.SampleDesc.Count = sampleCount;
260 desc.SampleDesc.Quality = qualityLevels;
261 desc.Usage = D3D11_USAGE_DEFAULT;
262 desc.BindFlags = BindFlags;
263 desc.CPUAccessFlags = 0;
264 desc.MiscFlags = 0;
265
266 HRESULT hr = device.CreateTexture2D(&desc, nullptr, &m_texture);
267
268 if (FAILED(hr)) {
269 ERROR("Texture", "init",
270 ("Failed to create texture with specified params. HRESULT: " + std::to_string(hr)).c_str());
271 return hr;
272 }
273
274 return S_OK;
275}
276
277HRESULT
278Texture::init(Device& device, Texture& textureRef, DXGI_FORMAT format) {
279 if (!device.m_device) {
280 ERROR("Texture", "init", "Device is null.");
281 return E_POINTER;
282 }
283 if (!textureRef.m_texture) {
284 ERROR("Texture", "init", "Texture is null.");
285 return E_POINTER;
286 }
287
288 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
289 srvDesc.Format = format;
290 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
291 srvDesc.Texture2D.MipLevels = 1;
292 srvDesc.Texture2D.MostDetailedMip = 0;
293
294 HRESULT hr = device.m_device->CreateShaderResourceView(textureRef.m_texture,
295 &srvDesc,
297
298 if (FAILED(hr)) {
299 ERROR("Texture", "init",
300 ("Failed to create shader resource view for PNG textures. HRESULT: " + std::to_string(hr)).c_str());
301 return hr;
302 }
303
304 return S_OK;
305}
306
307void
309
310}
311
312void
314 unsigned int StartSlot,
315 unsigned int NumViews) {
316 if (!deviceContext.m_deviceContext) {
317 ERROR("Texture", "render", "Device Context is null.");
318 return;
319 }
320
321 if (m_textureFromImg) {
322 deviceContext.PSSetShaderResources(StartSlot, NumViews, &m_textureFromImg);
323 }
324}
325
326void
328 if (m_texture != nullptr) {
330 }
331 if (m_textureFromImg != nullptr) {
333 }
334}
335
336HRESULT
338 DeviceContext& deviceContext,
339 const std::array<std::string, 6>& facePaths,
340 bool generateMips) {
341 destroy();
342
343 stbi_set_flip_vertically_on_load(false);
344
345 int width = 0, height = 0, channels = 0;
346 std::array<unsigned char*, 6> facePixels{};
347 facePixels.fill(nullptr);
348
349 for (int i = 0; i < 6; ++i) {
350 int w = 0, h = 0, c = 0;
351 facePixels[i] = stbi_load(facePaths[i].c_str(), &w, &h, &c, 4);
352 if (!facePixels[i]) {
353 for (int k = 0; k < i; ++k) {
354 if (facePixels[k]) {
355 stbi_image_free(facePixels[k]);
356 }
357 }
358 return E_FAIL;
359 }
360
361 if (i == 0) {
362 width = w;
363 height = h;
364 }
365 else if (w != width || h != height) {
366 ERROR("Texture", "CreateCubemap", "All cubemap faces must have the same dimensions.");
367 for (int k = 0; k <= i; ++k) {
368 if (facePixels[k]) {
369 stbi_image_free(facePixels[k]);
370 }
371 }
372 return E_FAIL;
373 }
374 }
375
376 D3D11_TEXTURE2D_DESC texDesc{};
377 texDesc.Width = static_cast<unsigned int>(width);
378 texDesc.Height = static_cast<unsigned int>(height);
379 texDesc.MipLevels = generateMips ? 0 : 1;
380 texDesc.ArraySize = 6;
381 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
382 texDesc.SampleDesc.Count = 1;
383 texDesc.SampleDesc.Quality = 0;
384 texDesc.Usage = D3D11_USAGE_DEFAULT;
385 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | (generateMips ? D3D11_BIND_RENDER_TARGET : 0);
386 texDesc.CPUAccessFlags = 0;
387 texDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE | (generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0);
388
389 HRESULT hr = S_OK;
390
391 if (!generateMips) {
392 std::array<D3D11_SUBRESOURCE_DATA, 6> initData{};
393 for (int face = 0; face < 6; ++face)
394 {
395 initData[face].pSysMem = facePixels[face];
396 initData[face].SysMemPitch = static_cast<unsigned int>(width * 4);
397 initData[face].SysMemSlicePitch = 0;
398 }
399
400 hr = device.CreateTexture2D(&texDesc, initData.data(), &m_texture);
401 if (FAILED(hr)) {
402 for (auto* p : facePixels) {
403 if (p) {
404 stbi_image_free(p);
405 }
406 }
407 return hr;
408 }
409 }
410 else {
411 hr = device.CreateTexture2D(&texDesc, nullptr, &m_texture);
412 if (FAILED(hr)) {
413 for (auto* p : facePixels) {
414 if (p) {
415 stbi_image_free(p);
416 }
417 }
418 return hr;
419 }
420
421 UINT mipCount = 1 + (UINT)floor(log2(max(width, height)));
422
423 for (UINT face = 0; face < 6; ++face)
424 {
425 UINT sub = D3D11CalcSubresource(0, face, mipCount);
426
427 deviceContext.UpdateSubresource(
428 m_texture,
429 sub,
430 nullptr,
431 facePixels[face],
432 width * 4,
433 0
434 );
435 }
436 }
437
438 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
439 srvDesc.Format = texDesc.Format;
440 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
441 srvDesc.TextureCube.MostDetailedMip = 0;
442 srvDesc.TextureCube.MipLevels = generateMips ? (unsigned int)-1 : 1;
443
444 hr = device.m_device->CreateShaderResourceView(m_texture, &srvDesc, &m_textureFromImg);
445
446 if (FAILED(hr)) {
447 for (auto* p : facePixels) {
448 if (p) {
449 stbi_image_free(p);
450 }
451 }
452 destroy();
453 return hr;
454 }
455
456 if (generateMips)
457 {
458 deviceContext.m_deviceContext->GenerateMips(m_textureFromImg);
459 }
460
461 for (auto* p : facePixels) {
462 if (p) {
463 stbi_image_free(p);
464 }
465 }
466
467 m_textureName = "Cubemap";
468
469 return S_OK;
470}
471
472
Declara la API de DeviceContext dentro del subsistema Core.
Declara la API de Device dentro del subsistema Core.
#define SAFE_RELEASE(x)
#define ERROR(classObj, method, errorMSG)
ExtensionType
@ JPG
@ DDS
@ PNG
Declara la API de Texture dentro del subsistema Core.
void PSSetShaderResources(unsigned int StartSlot, unsigned int NumViews, ID3D11ShaderResourceView *const *ppShaderResourceViews)
Asigna Shader Resource Views a la etapa de Pixel Shader.
ID3D11DeviceContext * m_deviceContext
Puntero al contexto inmediato de Direct3D 11.
void UpdateSubresource(ID3D11Resource *pDstResource, unsigned int DstSubresource, const D3D11_BOX *pDstBox, const void *pSrcData, unsigned int SrcRowPitch, unsigned int SrcDepthPitch)
Copia datos desde CPU hacia un recurso en GPU.
Encapsula un ID3D11Device y facilita la creación de recursos gráficos en Direct3D 11.
Definition Device.h:21
HRESULT CreateTexture2D(const D3D11_TEXTURE2D_DESC *pDesc, const D3D11_SUBRESOURCE_DATA *pInitialData, ID3D11Texture2D **ppTexture2D)
Crea una textura 2D.
Definition Device.cpp:42
ID3D11Device * m_device
Puntero al dispositivo Direct3D 11.
Definition Device.h:146
Encapsula una textura 2D en Direct3D 11, incluyendo su recurso y vista como Shader Resource.
Definition Texture.h:24
void render(DeviceContext &deviceContext, unsigned int StartSlot, unsigned int NumViews)
Asigna la textura al pipeline de render.
Definition Texture.cpp:313
void update()
Actualiza el contenido de la textura.
Definition Texture.cpp:308
HRESULT init(Device &device, const std::string &textureName, ExtensionType extensionType)
Inicializa una textura cargada desde archivo.
Definition Texture.cpp:182
void destroy()
Libera los recursos de la textura.
Definition Texture.cpp:327
HRESULT CreateCubemap(Device &device, DeviceContext &deviceContext, const std::array< std::string, 6 > &facePaths, bool generateMips)
Definition Texture.cpp:337
ID3D11Texture2D * m_texture
Recurso base de la textura en GPU.
Definition Texture.h:163
std::string m_textureName
Nombre o ruta de la textura (si proviene de archivo).
Definition Texture.h:175
ID3D11ShaderResourceView * m_textureFromImg
Vista de la textura como recurso de shader.
Definition Texture.h:170
constexpr uint32_t kTextureCacheVersion
Definition Texture.cpp:16
bool GetFileWriteTime(const std::string &path, ULONGLONG &outWriteTime)
Definition Texture.cpp:24
bool SaveTextureCache(const std::string &cachePath, int width, int height, const unsigned char *data)
Definition Texture.cpp:53
std::string GetTextureCachePath(const std::string &sourcePath)
Definition Texture.cpp:37
HRESULT CreateTextureFromRGBA(Device &device, int width, int height, const unsigned char *data, ID3D11Texture2D **outTexture, ID3D11ShaderResourceView **outSRV)
Definition Texture.cpp:98
HRESULT InitTextureFromImage(Device &device, const std::string &fullPath, Texture &texture)
Definition Texture.cpp:138
constexpr uint32_t kTextureCacheMagic
Definition Texture.cpp:15
bool IsTextureCacheUpToDate(const std::string &sourcePath, const std::string &cachePath)
Definition Texture.cpp:41
bool LoadTextureCache(const std::string &cachePath, CachedTextureData &outTexture)
Definition Texture.cpp:69