Unravel Engine C++ Reference
Loading...
Searching...
No Matches
bgfx_utils.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011-2023 Branimir Karadzic. All rights reserved.
3 * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
4 */
5
6#include "common.h"
7
8#include <tinystl/allocator.h>
9#include <tinystl/string.h>
10#include <tinystl/vector.h>
11namespace stl = tinystl;
12
13#include "entry/entry.h"
14#include <bgfx/bgfx.h>
15#include <bx/commandline.h>
16#include <bx/endian.h>
17#include <bx/math.h>
18#include <bx/readerwriter.h>
19#include <bx/string.h>
20
21#include "bgfx_utils.h"
22
23#include <bimg/bimg.h>
24#include <bimg/decode.h>
25
26#include <bgfx/bgfx.h>
27#include <bx/bx.h>
28#include <bx/file.h>
29#include <bx/sort.h>
30
31#include <time.h>
32namespace entry
33{
34namespace
35{
36
37bx::AllocatorI* getDefaultAllocator()
38{
39 static bx::DefaultAllocator allocator;
40 return &allocator;
41}
42
43static bx::AllocatorI* g_allocator = getDefaultAllocator();
44typedef bx::StringT<&g_allocator> String;
45
46class FileReader : public bx::FileReader
47{
48 typedef bx::FileReader super;
49
50public:
51 virtual bool open(const bx::FilePath& _filePath, bx::Error* _err) override
52 {
53 return super::open(_filePath, _err);
54 }
55};
56
57class FileWriter : public bx::FileWriter
58{
59 typedef bx::FileWriter super;
60
61public:
62 virtual bool open(const bx::FilePath& _filePath, bool _append, bx::Error* _err) override
63 {
64 return super::open(_filePath, _append, _err);
65 }
66};
67
68bx::AllocatorI* getAllocator()
69{
70 return g_allocator;
71}
72
73} // namespace
74} // namespace entry
75
76void* load(bx::FileReaderI* _reader, bx::AllocatorI* _allocator, const bx::FilePath& _filePath, uint32_t* _size)
77{
78 if(bx::open(_reader, _filePath))
79 {
80 uint32_t size = (uint32_t)bx::getSize(_reader);
81 void* data = bx::alloc(_allocator, size);
82 bx::read(_reader, data, size, bx::ErrorAssert{});
83 bx::close(_reader);
84 if(NULL != _size)
85 {
86 *_size = size;
87 }
88 return data;
89 }
90 else
91 {
92 DBG("Failed to open: %s.", _filePath);
93 }
94
95 if(NULL != _size)
96 {
97 *_size = 0;
98 }
99
100 return NULL;
101}
102
103void* load(const bx::FilePath& _filePath, uint32_t* _size)
104{
105 entry::FileReader reader;
106 return load(&reader, entry::getAllocator(), _filePath, _size);
107}
108
109void unload(void* _ptr)
110{
111 bx::free(entry::getAllocator(), _ptr);
112}
113
114static const bgfx::Memory* loadMem(bx::FileReaderI* _reader, const char* _filePath)
115{
116 if(bx::open(_reader, _filePath))
117 {
118 uint32_t size = (uint32_t)bx::getSize(_reader);
119 const bgfx::Memory* mem = bgfx::alloc(size + 1);
120 bx::read(_reader, mem->data, size, bx::ErrorAssert{});
121 bx::close(_reader);
122 mem->data[mem->size - 1] = '\0';
123 return mem;
124 }
125
126 DBG("Failed to load %s.", _filePath);
127 return NULL;
128}
129
130static void* loadMem(bx::FileReaderI* _reader, bx::AllocatorI* _allocator, const bx::FilePath& _filePath, uint32_t* _size)
131{
132 if(bx::open(_reader, _filePath))
133 {
134 uint32_t size = (uint32_t)bx::getSize(_reader);
135 void* data = bx::alloc(_allocator, size);
136 bx::read(_reader, data, size, bx::ErrorAssert{});
137 bx::close(_reader);
138
139 if(NULL != _size)
140 {
141 *_size = size;
142 }
143 return data;
144 }
145
146 DBG("Failed to load %s.", _filePath);
147 return NULL;
148}
149
150static bgfx::ShaderHandle loadShader(bx::FileReaderI* _reader, const bx::StringView& _name)
151{
152 bx::FilePath filePath("shaders/");
153
154 switch (bgfx::getRendererType() )
155 {
156 case bgfx::RendererType::Noop:
157 case bgfx::RendererType::Direct3D11:
158 case bgfx::RendererType::Direct3D12: filePath.join("dx11"); break;
159 case bgfx::RendererType::Agc:
160 case bgfx::RendererType::Gnm: filePath.join("pssl"); break;
161 case bgfx::RendererType::Metal: filePath.join("metal"); break;
162 case bgfx::RendererType::Nvn: filePath.join("nvn"); break;
163 case bgfx::RendererType::OpenGL: filePath.join("glsl"); break;
164 case bgfx::RendererType::OpenGLES: filePath.join("essl"); break;
165 case bgfx::RendererType::Vulkan: filePath.join("spirv"); break;
166 case bgfx::RendererType::WebGPU: filePath.join("wgsl"); break;
167
168 case bgfx::RendererType::Count:
169 BX_ASSERT(false, "You should not be here!");
170 break;
171 }
172
173 char fileName[512];
174 bx::strCopy(fileName, BX_COUNTOF(fileName), _name);
175 bx::strCat(fileName, BX_COUNTOF(fileName), ".bin");
176
177 filePath.join(fileName);
178
179 bgfx::ShaderHandle handle = bgfx::createShader(loadMem(_reader, filePath.getCPtr() ) );
180 bgfx::setName(handle, _name.getPtr(), _name.getLength() );
181
182 return handle;
183}
184
185bgfx::ShaderHandle loadShader(const bx::StringView& _name)
186{
187 entry::FileReader reader;
188 return loadShader(&reader, _name);
189}
190
191bgfx::ShaderHandle loadShader(const bx::FilePath& _filePath)
192{
193 entry::FileReader reader;
194 bgfx::ShaderHandle handle = bgfx::createShader(loadMem(&reader, _filePath.getCPtr() ) );
195 bgfx::setName(handle, _filePath.getFileName().getPtr(), int32_t(_filePath.getFileName().getLength() ) );
196 return handle;
197}
198
199bgfx::ProgramHandle loadProgram(bx::FileReaderI* _reader, const bx::StringView& _vsName, const bx::StringView& _fsName)
200{
201 bgfx::ShaderHandle vsh = loadShader(_reader, _vsName);
202 bgfx::ShaderHandle fsh = BGFX_INVALID_HANDLE;
203 if(NULL != _fsName)
204 {
205 fsh = loadShader(_reader, _fsName);
206 }
207
208 return bgfx::createProgram(vsh, fsh, true /* destroy shaders when program is destroyed */);
209}
210
211bgfx::ProgramHandle loadProgram(const bx::StringView& _vsName, const bx::StringView& _fsName)
212{
213 entry::FileReader reader;
214 return loadProgram(&reader, _vsName, _fsName);
215}
216
217static void imageReleaseCb(void* _ptr, void* _userData)
218{
219 BX_UNUSED(_ptr);
220 bimg::ImageContainer* imageContainer = (bimg::ImageContainer*)_userData;
221 bimg::imageFree(imageContainer);
222}
223
224static bgfx::TextureHandle loadTextureFromContainer(bimg::ImageContainer* imageContainer,
225 uint64_t _flags,
226 bgfx::TextureInfo* _info,
227 const TexturePreCreateFn& _preCreate = nullptr)
228{
229 if(NULL == imageContainer)
230 {
231 return BGFX_INVALID_HANDLE;
232 }
233
234 const bgfx::Memory* mem = bgfx::makeRef(
235 imageContainer->m_data,
236 imageContainer->m_size,
238 imageContainer);
239
240 bgfx::TextureInfo info;
241 bgfx::calcTextureSize(
242 info,
243 uint16_t(imageContainer->m_width),
244 uint16_t(imageContainer->m_height),
245 uint16_t(imageContainer->m_depth),
246 imageContainer->m_cubeMap,
247 1 < imageContainer->m_numMips,
248 imageContainer->m_numLayers,
249 bgfx::TextureFormat::Enum(imageContainer->m_format));
250
251 if(NULL != _info)
252 {
253 *_info = info;
254 }
255
256 // The image is parsed and its exact GPU footprint is known; give the caller a chance to
257 // reject the allocation before the create lands.
258 if(_preCreate && !_preCreate(info))
259 {
260 bimg::imageFree(imageContainer);
261 return BGFX_INVALID_HANDLE;
262 }
263
264 if(imageContainer->m_cubeMap)
265 {
266 return bgfx::createTextureCube(
267 uint16_t(imageContainer->m_width),
268 1 < imageContainer->m_numMips,
269 imageContainer->m_numLayers,
270 bgfx::TextureFormat::Enum(imageContainer->m_format),
271 _flags,
272 mem);
273 }
274
275 if(1 < imageContainer->m_depth)
276 {
277 return bgfx::createTexture3D(
278 uint16_t(imageContainer->m_width),
279 uint16_t(imageContainer->m_height),
280 uint16_t(imageContainer->m_depth),
281 1 < imageContainer->m_numMips,
282 bgfx::TextureFormat::Enum(imageContainer->m_format),
283 _flags,
284 mem);
285 }
286
287 if(bgfx::isTextureValid(0, false, imageContainer->m_numLayers, bgfx::TextureFormat::Enum(imageContainer->m_format), _flags))
288 {
289 return bgfx::createTexture2D(
290 uint16_t(imageContainer->m_width),
291 uint16_t(imageContainer->m_height),
292 1 < imageContainer->m_numMips,
293 imageContainer->m_numLayers,
294 bgfx::TextureFormat::Enum(imageContainer->m_format),
295 _flags,
296 mem);
297 }
298
299 return BGFX_INVALID_HANDLE;
300}
301
302namespace
303{
304
305auto can_flip_normal_y_format(bimg::TextureFormat::Enum _format) -> bool
306{
307 if(bimg::isCompressed(_format) || bimg::isFloat(_format))
308 {
309 return false;
310 }
311
312 return bimg::getUnpack(_format) != nullptr && bimg::getPack(_format) != nullptr;
313}
314
315auto flip_normal_y_mip_ldr(bimg::ImageMip& _mip, bimg::TextureFormat::Enum _format) -> bool
316{
317 const bimg::UnpackFn unpack = bimg::getUnpack(_format);
318 const bimg::PackFn pack = bimg::getPack(_format);
319 if(nullptr == unpack || nullptr == pack)
320 {
321 return false;
322 }
323
324 const uint32_t bpp = bimg::getBitsPerPixel(_format);
325 if(0 == bpp || (bpp % 8) != 0)
326 {
327 return false;
328 }
329
330 const uint32_t bytes_per_pixel = bpp / 8;
331 const uint32_t width = _mip.m_width;
332 const uint32_t height = _mip.m_height;
333 const uint32_t depth = bx::max<uint32_t>(1, _mip.m_depth);
334 const uint32_t row_stride = width * bytes_per_pixel;
335 const uint32_t slice_stride = row_stride * height;
336
337 uint8_t* slice = const_cast<uint8_t*>(_mip.m_data);
338 for(uint32_t zz = 0; zz < depth; ++zz)
339 {
340 uint8_t* row = slice + zz * slice_stride;
341 for(uint32_t yy = 0; yy < height; ++yy)
342 {
343 uint8_t* pixel = row;
344 for(uint32_t xx = 0; xx < width; ++xx)
345 {
346 float rgba[4];
347 unpack(rgba, pixel);
348 rgba[1] = 1.0f - rgba[1];
349 pack(pixel, rgba);
350 pixel += bytes_per_pixel;
351 }
352 row += row_stride;
353 }
354 }
355
356 return true;
357}
358
359auto flip_normal_y_all_mips_ldr(bimg::ImageContainer* _image) -> bool
360{
361 if(!can_flip_normal_y_format(_image->m_format))
362 {
363 return false;
364 }
365
366 for(uint8_t mip = 0; mip < _image->m_numMips; ++mip)
367 {
368 for(uint16_t layer = 0; layer < _image->m_numLayers; ++layer)
369 {
370 bimg::ImageMip mipData;
371 if(!bimg::imageGetRawData(*_image, layer, mip, _image->m_data, _image->m_size, mipData))
372 {
373 continue;
374 }
375
376 if(!flip_normal_y_mip_ldr(mipData, _image->m_format))
377 {
378 return false;
379 }
380 }
381 }
382
383 return true;
384}
385
386auto ensure_rgba8_opaque_alpha(bimg::ImageContainer* _image) -> void
387{
388 if(nullptr == _image)
389 {
390 return;
391 }
392
393 if(_image->m_format != bimg::TextureFormat::RGBA8)
394 {
395 return;
396 }
397
398 for(uint8_t mip = 0; mip < _image->m_numMips; ++mip)
399 {
400 for(uint16_t layer = 0; layer < _image->m_numLayers; ++layer)
401 {
402 bimg::ImageMip mipData;
403 if(!bimg::imageGetRawData(*_image, layer, mip, _image->m_data, _image->m_size, mipData))
404 {
405 continue;
406 }
407
408 const uint32_t width = mipData.m_width;
409 const uint32_t height = mipData.m_height;
410 const uint32_t depth = bx::max<uint32_t>(1, mipData.m_depth);
411 const uint32_t row_stride = width * 4;
412 const uint32_t slice_stride = row_stride * height;
413
414 uint8_t* slice = const_cast<uint8_t*>(mipData.m_data);
415 for(uint32_t zz = 0; zz < depth; ++zz)
416 {
417 uint8_t* row = slice + zz * slice_stride;
418 for(uint32_t yy = 0; yy < height; ++yy)
419 {
420 uint8_t* pixel = row;
421 for(uint32_t xx = 0; xx < width; ++xx)
422 {
423 // PNG RGB sources often decode with alpha=0; make preview/texturec input opaque.
424 if(0 == pixel[3])
425 {
426 pixel[3] = 255;
427 }
428 pixel += 4;
429 }
430 row += row_stride;
431 }
432 }
433 }
434 }
435}
436
437} // namespace
438
439bool imageFlipTangentSpaceNormalY(bimg::ImageContainer*& _image)
440{
441 if(NULL == _image)
442 {
443 return false;
444 }
445
446 if(bimg::isCompressed(_image->m_format))
447 {
448 bimg::ImageContainer* decoded =
449 bimg::imageConvert(entry::getAllocator(), bimg::TextureFormat::RGBA8, *_image);
450 if(NULL == decoded)
451 {
452 return false;
453 }
454
455 bimg::imageFree(_image);
456 _image = decoded;
457 }
458
459 if(!flip_normal_y_all_mips_ldr(_image))
460 {
461 return false;
462 }
463
464 return true;
465}
466
467bool imagePrepareNormalMapBakePng(bimg::ImageContainer*& _image)
468{
469 if(nullptr == _image)
470 {
471 return false;
472 }
473
474 if(_image->m_format != bimg::TextureFormat::RGBA8)
475 {
476 bimg::ImageContainer* converted =
477 bimg::imageConvert(entry::getAllocator(), bimg::TextureFormat::RGBA8, *_image);
478 if(nullptr == converted)
479 {
480 return false;
481 }
482
483 bimg::imageFree(_image);
484 _image = converted;
485 }
486
487 ensure_rgba8_opaque_alpha(_image);
488 return true;
489}
490
491bgfx::TextureHandle loadTexture(const void* _data,
492 uint32_t _size,
493 uint64_t _flags,
494 uint8_t _skip,
495 bgfx::TextureInfo* _info,
496 bimg::Orientation::Enum* _orientation,
497 const char* _name,
498 bx::Error* _err,
499 const TexturePreCreateFn& _preCreate)
500{
501 BX_UNUSED(_skip);
502 bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
503
504 if (NULL != _info)
505 {
506 bx::memSet(_info, 0, sizeof(*_info) );
507 _info->format = bgfx::TextureFormat::Unknown;
508 }
509
510 if (NULL != _orientation)
511 {
512 *_orientation = bimg::Orientation::R0;
513 }
514
515 bx::Error localErr;
516 bx::Error* err = (NULL != _err) ? _err : &localErr;
517 bimg::ImageContainer* imageContainer = bimg::imageParse(entry::getAllocator(), _data, _size, bimg::TextureFormat::Count, err);
518 if(NULL != imageContainer)
519 {
520 if(NULL != _orientation)
521 {
522 *_orientation = imageContainer->m_orientation;
523 }
524
525 handle = loadTextureFromContainer(imageContainer, _flags, _info, _preCreate);
526
527 if(bgfx::isValid(handle) && NULL != _name)
528 {
529 bgfx::setName(handle, _name, int32_t(bx::strLen(_name)));
530 }
531 }
532
533 return handle;
534}
535
536bgfx::TextureHandle loadTexture(bx::FileReaderI* _reader,
537 const bx::FilePath& _filePath,
538 uint64_t _flags,
539 uint8_t _skip,
540 bgfx::TextureInfo* _info,
541 bimg::Orientation::Enum* _orientation,
542 bx::Error* _err,
543 const TexturePreCreateFn& _preCreate = nullptr)
544{
545 BX_UNUSED(_skip);
546 bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
547
548 if (NULL != _info)
549 {
550 bx::memSet(_info, 0, sizeof(*_info) );
551 _info->format = bgfx::TextureFormat::Unknown;
552 }
553
554 if (NULL != _orientation)
555 {
556 *_orientation = bimg::Orientation::R0;
557 }
558
559 uint32_t size;
560 void* data = load(_reader, entry::getAllocator(), _filePath, &size);
561 if(NULL != data)
562 {
563 const bx::StringView name(_filePath);
564 handle = loadTexture(data, size, _flags, _skip, _info, _orientation, name.getPtr(), _err, _preCreate);
565 unload(data);
566 }
567
568 return handle;
569}
570
571bgfx::TextureHandle loadTexture(const bx::FilePath& _filePath,
572 uint64_t _flags,
573 uint8_t _skip,
574 bgfx::TextureInfo* _info,
575 bimg::Orientation::Enum* _orientation,
576 bx::Error* _err,
577 const TexturePreCreateFn& _preCreate)
578{
579 entry::FileReader reader;
580 return loadTexture(&reader, _filePath, _flags, _skip, _info, _orientation, _err, _preCreate);
581}
582
583bimg::ImageContainer* imageLoad(const void* data, uint32_t size, bgfx::TextureFormat::Enum _dstFormat)
584{
585 return bimg::imageParse(entry::getAllocator(), data, size, bimg::TextureFormat::Enum(_dstFormat));
586}
587
588bimg::ImageContainer* imageLoad(const bx::FilePath& _filePath, bgfx::TextureFormat::Enum _dstFormat)
589{
590 entry::FileReader reader;
591
592 uint32_t size = 0;
593 void* data = loadMem(&reader, entry::getAllocator(), _filePath, &size);
594
595 if(data == nullptr)
596 {
597 return nullptr;
598 }
599
600 return bimg::imageParse(entry::getAllocator(), data, size, bimg::TextureFormat::Enum(_dstFormat));
601}
602
603bool imageParseInfo(const void* _data, uint32_t _size, bimg::ImageContainer& _info, bx::Error* _err)
604{
605 if(_data == nullptr || _size == 0)
606 {
607 return false;
608 }
609
610 return bimg::imageParseInfo(entry::getAllocator(), _info, _data, _size, _err);
611}
612
613bool imageParseInfo(const bx::FilePath& _filePath, bimg::ImageContainer& _info, bx::Error* _err)
614{
615 uint32_t size = 0;
616 entry::FileReader reader;
617 void* data = load(&reader, entry::getAllocator(), _filePath, &size);
618 if(data == nullptr)
619 {
620 return false;
621 }
622
623 const bool parsed = bimg::imageParseInfo(entry::getAllocator(), _info, data, size, _err);
624 unload(data);
625 return parsed;
626}
627
628void calcTangents(void* _vertices,
629 uint16_t _numVertices,
630 bgfx::VertexLayout _layout,
631 const uint16_t* _indices,
632 uint32_t _numIndices)
633{
634 struct PosTexcoord
635 {
636 float m_x;
637 float m_y;
638 float m_z;
639 float m_pad0;
640 float m_u;
641 float m_v;
642 float m_pad1;
643 float m_pad2;
644 };
645
646 float* tangents = new float[6 * _numVertices];
647 bx::memSet(tangents, 0, 6 * _numVertices * sizeof(float));
648
649 PosTexcoord v0;
650 PosTexcoord v1;
651 PosTexcoord v2;
652
653 for(uint32_t ii = 0, num = _numIndices / 3; ii < num; ++ii)
654 {
655 const uint16_t* indices = &_indices[ii * 3];
656 uint32_t i0 = indices[0];
657 uint32_t i1 = indices[1];
658 uint32_t i2 = indices[2];
659
660 bgfx::vertexUnpack(&v0.m_x, bgfx::Attrib::Position, _layout, _vertices, i0);
661 bgfx::vertexUnpack(&v0.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i0);
662
663 bgfx::vertexUnpack(&v1.m_x, bgfx::Attrib::Position, _layout, _vertices, i1);
664 bgfx::vertexUnpack(&v1.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i1);
665
666 bgfx::vertexUnpack(&v2.m_x, bgfx::Attrib::Position, _layout, _vertices, i2);
667 bgfx::vertexUnpack(&v2.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i2);
668
669 const float bax = v1.m_x - v0.m_x;
670 const float bay = v1.m_y - v0.m_y;
671 const float baz = v1.m_z - v0.m_z;
672 const float bau = v1.m_u - v0.m_u;
673 const float bav = v1.m_v - v0.m_v;
674
675 const float cax = v2.m_x - v0.m_x;
676 const float cay = v2.m_y - v0.m_y;
677 const float caz = v2.m_z - v0.m_z;
678 const float cau = v2.m_u - v0.m_u;
679 const float cav = v2.m_v - v0.m_v;
680
681 const float det = (bau * cav - bav * cau);
682 const float invDet = 1.0f / det;
683
684 const float tx = (bax * cav - cax * bav) * invDet;
685 const float ty = (bay * cav - cay * bav) * invDet;
686 const float tz = (baz * cav - caz * bav) * invDet;
687
688 const float bx = (cax * bau - bax * cau) * invDet;
689 const float by = (cay * bau - bay * cau) * invDet;
690 const float bz = (caz * bau - baz * cau) * invDet;
691
692 for(uint32_t jj = 0; jj < 3; ++jj)
693 {
694 float* tanu = &tangents[indices[jj] * 6];
695 float* tanv = &tanu[3];
696 tanu[0] += tx;
697 tanu[1] += ty;
698 tanu[2] += tz;
699
700 tanv[0] += bx;
701 tanv[1] += by;
702 tanv[2] += bz;
703 }
704 }
705
706 for(uint32_t ii = 0; ii < _numVertices; ++ii)
707 {
708 const bx::Vec3 tanu = bx::load<bx::Vec3>(&tangents[ii * 6]);
709 const bx::Vec3 tanv = bx::load<bx::Vec3>(&tangents[ii * 6 + 3]);
710
711 float nxyzw[4];
712 bgfx::vertexUnpack(nxyzw, bgfx::Attrib::Normal, _layout, _vertices, ii);
713
714 const bx::Vec3 normal = bx::load<bx::Vec3>(nxyzw);
715 const float ndt = bx::dot(normal, tanu);
716 const bx::Vec3 nxt = bx::cross(normal, tanu);
717 const bx::Vec3 tmp = bx::sub(tanu, bx::mul(normal, ndt));
718
719 float tangent[4];
720 bx::store(tangent, bx::normalize(tmp));
721 tangent[3] = bx::dot(nxt, tanv) < 0.0f ? -1.0f : 1.0f;
722
723 bgfx::vertexPack(tangent, true, bgfx::Attrib::Tangent, _layout, _vertices, ii);
724 }
725
726 delete[] tangents;
727}
728
729bool saveToFile(bgfx::ViewId viewId, const bx::FilePath& _filePath, bgfx::FrameBufferHandle fbo, uint32_t width, uint32_t height)
730{
731
732 auto input_tex = bgfx::getTexture(fbo);
733 // formats have one to one mapping
734 auto format = bgfx::TextureFormat::RGBA8;
735 auto bimg_format = static_cast<bimg::TextureFormat::Enum>(format);
736
737 bool result = false;
738
739 uint64_t flags = 0 | BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK | BGFX_SAMPLER_U_CLAMP |
740 BGFX_SAMPLER_V_CLAMP;
741 auto blit_tex = bgfx::createTexture2D(width, height, false, 1, format, flags, nullptr);
742
743 bgfx::TextureInfo info;
744 bgfx::calcTextureSize(info, width, height, 1, false, false, 1, format);
745
746 // Blit and read
747 bgfx::touch(viewId);
748 bgfx::blit(viewId, blit_tex, 0, 0, input_tex);
749
750 // Allocate memory for the texture data
751
752 tinystl::vector<uint8_t> input(info.storageSize);
753
754 // Read the frame buffer data
755 uint32_t frameNumber = bgfx::readTexture(blit_tex, input.data());
756
757
758 // Wait until the data is available (frameNumber indicates when)
759 while(bgfx::frame() != frameNumber)
760 {
761 // You can perform other tasks here if needed
762 break;
763 }
764
765 bx::FilePath filePath(_filePath);
766
767 if(bx::makeAll(filePath.getPath()))
768 {
769 bx::FileWriter writer;
770 if(bx::open(&writer, filePath))
771 {
772 bx::Error err;
773 bimg::imageWritePng(&writer,
774 info.width,
775 info.height,
776 info.width * (info.bitsPerPixel / 8),
777 input.data(),
778 bimg_format,
779 false,
780 &err);
781 result = true;
782 bx::close(&writer);
783 }
784 }
785
786 return result;
787}
788
789bool imageSave(const char* saveAs, bimg::ImageContainer* image)
790{
791 if (!image)
792 {
793 return false;
794 }
795 // Write the image to file based on its extension
796 bx::FileWriter writer;
797 bx::Error err;
798
799 if (bx::open(&writer, saveAs, false, &err))
800 {
801 if (!bx::strFindI(saveAs, "tga").isEmpty())
802 {
803 bimg::imageWriteTga(&writer, image->m_width, image->m_height, image->m_width * 4, image->m_data, false, false, &err);
804 }
805 else if (!bx::strFindI(saveAs, "ktx").isEmpty())
806 {
807 bimg::imageWriteKtx(&writer, *image, image->m_data, image->m_size, &err);
808
809 }
810 else if (!bx::strFindI(saveAs, "dds").isEmpty())
811 {
812 bimg::imageWriteDds(&writer, *image, image->m_data, image->m_size, &err);
813
814 }
815 else if (!bx::strFindI(saveAs, "png").isEmpty())
816 {
817 if (image->m_format != bimg::TextureFormat::RGBA8)
818 {
819
820 auto converted = bimg::imageConvert(entry::getAllocator(), bimg::TextureFormat::RGBA8, *image);
821 if(converted)
822 {
823 bimg::ImageMip mip;
824 bimg::imageGetRawData(*converted, 0, 0, converted->m_data, converted->m_size, mip);
825 bimg::imageWritePng(&writer
826 , mip.m_width
827 , mip.m_height
828 , mip.m_width*4
829 , mip.m_data
830 , converted->m_format
831 , false
832 , &err
833 );
834 bimg::imageFree(converted);
835 }
836 else
837 {
838 //err.setError(bx::ErrorResult())
839 }
840 //help("Incompatible image texture format. image PNG format must be RGBA8.", err);
841 }
842 else
843 {
844 bimg::ImageMip mip;
845 bimg::imageGetRawData(*image, 0, 0, image->m_data, image->m_size, mip);
846 bimg::imageWritePng(&writer
847 , mip.m_width
848 , mip.m_height
849 , mip.m_width*4
850 , mip.m_data
851 , image->m_format
852 , false
853 , &err
854 );
855 }
856
857 }
858 else if (!bx::strFindI(saveAs, "exr").isEmpty())
859 {
860 bimg::ImageMip mip;
861 bimg::imageGetRawData(*image, 0, 0, image->m_data, image->m_size, mip);
862 bimg::imageWriteExr(&writer
863 , mip.m_width
864 , mip.m_height
865 , mip.m_width*8
866 , mip.m_data
867 , image->m_format
868 , false
869 , &err
870 );
871 }
872 else if (!bx::strFindI(saveAs, "hdr").isEmpty())
873 {
874 bimg::ImageMip mip;
875 bimg::imageGetRawData(*image, 0, 0, image->m_data, image->m_size, mip);
876 bimg::imageWriteHdr(&writer
877 , mip.m_width
878 , mip.m_height
879 , mip.m_width*getBitsPerPixel(mip.m_format)/8
880 , mip.m_data
881 , image->m_format
882 , false
883 , &err
884 );
885 }
886
887
888 bx::close(&writer);
889 }
890
891 return err.isOk();
892
893}
uint32_t width
uint32_t height
gfx::texture_format format
static const bgfx::Memory * loadMem(bx::FileReaderI *_reader, const char *_filePath)
static bgfx::ShaderHandle loadShader(bx::FileReaderI *_reader, const bx::StringView &_name)
static bgfx::TextureHandle loadTextureFromContainer(bimg::ImageContainer *imageContainer, uint64_t _flags, bgfx::TextureInfo *_info, const TexturePreCreateFn &_preCreate=nullptr)
bool imageParseInfo(const void *_data, uint32_t _size, bimg::ImageContainer &_info, bx::Error *_err)
void calcTangents(void *_vertices, uint16_t _numVertices, bgfx::VertexLayout _layout, const uint16_t *_indices, uint32_t _numIndices)
bimg::ImageContainer * imageLoad(const void *data, uint32_t size, bgfx::TextureFormat::Enum _dstFormat)
bool imageSave(const char *saveAs, bimg::ImageContainer *image)
bool imageFlipTangentSpaceNormalY(bimg::ImageContainer *&_image)
void * load(bx::FileReaderI *_reader, bx::AllocatorI *_allocator, const bx::FilePath &_filePath, uint32_t *_size)
static void imageReleaseCb(void *_ptr, void *_userData)
bool imagePrepareNormalMapBakePng(bimg::ImageContainer *&_image)
Prepare a flipped normal map for PNG bake export (RGBA8, opaque alpha when unused).
void unload(void *_ptr)
bgfx::ProgramHandle loadProgram(bx::FileReaderI *_reader, const bx::StringView &_vsName, const bx::StringView &_fsName)
bool saveToFile(bgfx::ViewId viewId, const bx::FilePath &_filePath, bgfx::FrameBufferHandle fbo, uint32_t width, uint32_t height)
bgfx::TextureHandle loadTexture(const void *_data, uint32_t _size, uint64_t _flags, uint8_t _skip, bgfx::TextureInfo *_info, bimg::Orientation::Enum *_orientation, const char *_name, bx::Error *_err, const TexturePreCreateFn &_preCreate)
std::function< bool(const bgfx::TextureInfo &_info)> TexturePreCreateFn
Definition bgfx_utils.h:28
#define DBG(_format,...)
Definition dbg.h:14
math::vec3 normal
Definition defaults.cpp:53
std::string name
Definition hub.cpp:33
gfx::texture_handle blit_tex
Definition mcp_async.cpp:32
Definition imgui.h:25
std::vector< uint32_t > indices
gfx::uniform_handle handle
Definition uniform.cpp:9
float size