From 25dd8377d52b497ea7cfeca403ba87883b2f3603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Thu, 9 Mar 2017 21:20:45 -0800 Subject: [PATCH] examples/common: Cleanup dealing with textures. --- examples/32-particles/particles.cpp | 14 + examples/common/bgfx_utils.cpp | 333 +++------------------ examples/common/bgfx_utils.h | 26 ++ examples/common/image.cpp | 394 +++++++++++++++++++++++++ examples/common/image.h | 62 ++++ examples/common/ps/particle_system.cpp | 129 +++++++- examples/common/ps/particle_system.h | 19 +- scripts/texturev.lua | 1 - src/image.cpp | 13 +- 9 files changed, 682 insertions(+), 309 deletions(-) create mode 100644 examples/common/image.cpp create mode 100644 examples/common/image.h diff --git a/examples/32-particles/particles.cpp b/examples/32-particles/particles.cpp index 4f7cc0bd1..a1f6f7fe0 100644 --- a/examples/32-particles/particles.cpp +++ b/examples/32-particles/particles.cpp @@ -249,9 +249,23 @@ class Particles : public entry::AppI psInit(); + bgfx::ImageContainer* image = imageLoad( + "textures/particle.ktx" + , bgfx::TextureFormat::BGRA8 + ); + + EmitterSpriteHandle sprite = psCreateSprite( + uint16_t(image->m_width) + , uint16_t(image->m_height) + , image->m_data + ); + + bgfx::imageFree(image); + for (uint32_t ii = 0; ii < BX_COUNTOF(m_emitter); ++ii) { m_emitter[ii].create(); + m_emitter[ii].m_uniforms.m_handle = sprite; } imguiCreate(); diff --git a/examples/common/bgfx_utils.cpp b/examples/common/bgfx_utils.cpp index d49cbc5c2..edea94caf 100644 --- a/examples/common/bgfx_utils.cpp +++ b/examples/common/bgfx_utils.cpp @@ -19,27 +19,6 @@ namespace stl = tinystl; #include "entry/entry.h" #include -BX_PRAGMA_DIAGNOSTIC_PUSH() -BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wtype-limits") -BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-parameter") -BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-value") -BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: '' : unreferenced formal parameter -#if BX_PLATFORM_EMSCRIPTEN -# include -#endif // BX_PLATFORM_EMSCRIPTEN -#define MINIZ_NO_STDIO -#define TINYEXR_IMPLEMENTATION -#include -BX_PRAGMA_DIAGNOSTIC_POP() - -#define LODEPNG_NO_COMPILE_ENCODER -#define LODEPNG_NO_COMPILE_DISK -#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS -#define LODEPNG_NO_COMPILE_ERROR_TEXT -#define LODEPNG_NO_COMPILE_ALLOCATORS -#define LODEPNG_NO_COMPILE_CPP -#include - #include "bgfx_utils.h" void* load(bx::FileReaderI* _reader, bx::AllocatorI* _allocator, const char* _filePath, uint32_t* _size) @@ -167,303 +146,73 @@ bgfx::ProgramHandle loadProgram(const char* _vsName, const char* _fsName) return loadProgram(entry::getFileReader(), _vsName, _fsName); } -typedef unsigned char stbi_uc; -extern "C" stbi_uc* stbi_load_from_memory(stbi_uc const* _buffer, int _len, int* _x, int* _y, int* _comp, int _req_comp); -extern "C" void stbi_image_free(void* _ptr); -extern void lodepng_free(void* _ptr); - -static void exrRelease(void* _ptr) +static void imageReleaseCb(void* _ptr, void* _userData) { - BX_FREE(entry::getAllocator(), _ptr); + BX_UNUSED(_ptr); + bgfx::ImageContainer* imageContainer = (bgfx::ImageContainer*)_userData; + bgfx::imageFree(imageContainer); } bgfx::TextureHandle loadTexture(bx::FileReaderI* _reader, const char* _filePath, uint32_t _flags, uint8_t _skip, bgfx::TextureInfo* _info) { - if (NULL != bx::stristr(_filePath, ".dds") - || NULL != bx::stristr(_filePath, ".pvr") - || NULL != bx::stristr(_filePath, ".ktx") ) - { - const bgfx::Memory* mem = loadMem(_reader, _filePath); - if (NULL != mem) - { - return bgfx::createTexture(mem, _flags, _skip, _info); - } - - bgfx::TextureHandle handle = BGFX_INVALID_HANDLE; - DBG("Failed to load %s.", _filePath); - return handle; - } - + BX_UNUSED(_skip); bgfx::TextureHandle handle = BGFX_INVALID_HANDLE; - bx::AllocatorI* allocator = entry::getAllocator(); - uint32_t size = 0; - void* data = loadMem(_reader, allocator, _filePath, &size); + uint32_t size; + void* data = load(_reader, entry::getAllocator(), _filePath, &size); if (NULL != data) { - bgfx::TextureFormat::Enum format = bgfx::TextureFormat::RGBA8; - uint32_t bpp = 32; + bgfx::ImageContainer* imageContainer = bgfx::imageParse(entry::getAllocator(), data, size); - uint32_t width = 0; - uint32_t height = 0; - - typedef void (*ReleaseFn)(void* _ptr); - ReleaseFn release = stbi_image_free; - - uint8_t* out = NULL; - static uint8_t pngMagic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0d, 0x0a }; - - if (0 == bx::memCmp(data, pngMagic, sizeof(pngMagic) ) ) + if (NULL != imageContainer) { - release = lodepng_free; + const bgfx::Memory* mem = bgfx::makeRef( + imageContainer->m_data + , imageContainer->m_size + , imageReleaseCb + , imageContainer + ); + unload(data); - unsigned error; - LodePNGState state; - lodepng_state_init(&state); - state.decoder.color_convert = 0; - error = lodepng_decode(&out, &width, &height, &state, (uint8_t*)data, size); - - if (0 == error) + if (imageContainer->m_cubeMap) { - switch (state.info_raw.bitdepth) - { - case 8: - switch (state.info_raw.colortype) - { - case LCT_GREY: - format = bgfx::TextureFormat::R8; - bpp = 8; - break; - - case LCT_GREY_ALPHA: - format = bgfx::TextureFormat::RG8; - bpp = 16; - break; - - case LCT_RGB: - format = bgfx::TextureFormat::RGB8; - bpp = 24; - break; - - case LCT_RGBA: - format = bgfx::TextureFormat::RGBA8; - bpp = 32; - break; - - case LCT_PALETTE: - format = bgfx::TextureFormat::R8; - bpp = 8; - break; - } - break; - - case 16: - switch (state.info_raw.colortype) - { - case LCT_GREY: - for (uint32_t ii = 0, num = width*height; ii < num; ++ii) - { - uint16_t* rgba = (uint16_t*)out + ii*4; - rgba[0] = bx::toHostEndian(rgba[0], false); - } - format = bgfx::TextureFormat::R16; - bpp = 16; - break; - - case LCT_GREY_ALPHA: - for (uint32_t ii = 0, num = width*height; ii < num; ++ii) - { - uint16_t* rgba = (uint16_t*)out + ii*4; - rgba[0] = bx::toHostEndian(rgba[0], false); - rgba[1] = bx::toHostEndian(rgba[1], false); - } - format = bgfx::TextureFormat::R16; - bpp = 16; - break; - - case LCT_RGBA: - for (uint32_t ii = 0, num = width*height; ii < num; ++ii) - { - uint16_t* rgba = (uint16_t*)out + ii*4; - rgba[0] = bx::toHostEndian(rgba[0], false); - rgba[1] = bx::toHostEndian(rgba[1], false); - rgba[2] = bx::toHostEndian(rgba[2], false); - rgba[3] = bx::toHostEndian(rgba[3], false); - } - format = bgfx::TextureFormat::RGBA16; - bpp = 64; - break; - - case LCT_RGB: - case LCT_PALETTE: - break; - } - break; - - default: - break; - } - } - - lodepng_state_cleanup(&state); - } - else - { - EXRVersion exrVersion; - int result = ParseEXRVersionFromMemory(&exrVersion, (uint8_t*)data, size); - if (TINYEXR_SUCCESS == result) - { - const char* err = NULL; - EXRHeader exrHeader; - result = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, (uint8_t*)data, size, &err); - if (TINYEXR_SUCCESS == result) - { - EXRImage exrImage; - InitEXRImage(&exrImage); - - result = LoadEXRImageFromMemory(&exrImage, &exrHeader, (uint8_t*)data, size, &err); - if (TINYEXR_SUCCESS == result) - { - uint8_t idxR = UINT8_MAX; - uint8_t idxG = UINT8_MAX; - uint8_t idxB = UINT8_MAX; - uint8_t idxA = UINT8_MAX; - for (uint8_t ii = 0, num = uint8_t(exrHeader.num_channels); ii < num; ++ii) - { - const EXRChannelInfo& channel = exrHeader.channels[ii]; - if (UINT8_MAX == idxR - && 0 == bx::strncmp(channel.name, "R") ) - { - idxR = ii; - } - else if (UINT8_MAX == idxG - && 0 == bx::strncmp(channel.name, "G") ) - { - idxG = ii; - } - else if (UINT8_MAX == idxB - && 0 == bx::strncmp(channel.name, "B") ) - { - idxB = ii; - } - else if (UINT8_MAX == idxA - && 0 == bx::strncmp(channel.name, "A") ) - { - idxA = ii; - } - } - - if (UINT8_MAX != idxR) - { - const bool asFloat = exrHeader.pixel_types[idxR] == TINYEXR_PIXELTYPE_FLOAT; - - uint32_t srcBpp = 32; - uint32_t dstBpp = asFloat ? 32 : 16; - format = asFloat ? bgfx::TextureFormat::R32F : bgfx::TextureFormat::R16F; - uint32_t stepR = 1; - uint32_t stepG = 0; - uint32_t stepB = 0; - uint32_t stepA = 0; - - if (UINT8_MAX != idxG) - { - srcBpp += 32; - dstBpp = asFloat ? 64 : 32; - format = asFloat ? bgfx::TextureFormat::RG32F : bgfx::TextureFormat::RG16F; - stepG = 1; - } - - if (UINT8_MAX != idxB) - { - srcBpp += 32; - dstBpp = asFloat ? 128 : 64; - format = asFloat ? bgfx::TextureFormat::RGBA32F : bgfx::TextureFormat::RGBA16F; - stepB = 1; - } - - if (UINT8_MAX != idxA) - { - srcBpp += 32; - dstBpp = asFloat ? 128 : 64; - format = asFloat ? bgfx::TextureFormat::RGBA32F : bgfx::TextureFormat::RGBA16F; - stepA = 1; - } - - release = exrRelease; - out = (uint8_t*)BX_ALLOC(allocator, exrImage.width * exrImage.height * dstBpp/8); - - const float zero = 0.0f; - const float* srcR = UINT8_MAX == idxR ? &zero : (const float*)(exrImage.images)[idxR]; - const float* srcG = UINT8_MAX == idxG ? &zero : (const float*)(exrImage.images)[idxG]; - const float* srcB = UINT8_MAX == idxB ? &zero : (const float*)(exrImage.images)[idxB]; - const float* srcA = UINT8_MAX == idxA ? &zero : (const float*)(exrImage.images)[idxA]; - - const uint32_t bytesPerPixel = dstBpp/8; - for (uint32_t ii = 0, num = exrImage.width * exrImage.height; ii < num; ++ii) - { - float rgba[4] = - { - *srcR, - *srcG, - *srcB, - *srcA, - }; - bx::memCopy(&out[ii * bytesPerPixel], rgba, bytesPerPixel); - - srcR += stepR; - srcG += stepG; - srcB += stepB; - srcA += stepA; - } - } - - FreeEXRImage(&exrImage); - } - - FreeEXRHeader(&exrHeader); - } + handle = bgfx::createTextureCube( + uint16_t(imageContainer->m_width) + , 1 < imageContainer->m_numMips + , imageContainer->m_numLayers + , imageContainer->m_format + , _flags + , mem + ); } else { - int comp = 0; - out = stbi_load_from_memory( (uint8_t*)data, size, (int*)&width, (int*)&height, &comp, 4); + handle = bgfx::createTexture2D( + uint16_t(imageContainer->m_width) + , uint16_t(imageContainer->m_height) + , 1 < imageContainer->m_numMips + , imageContainer->m_numLayers + , imageContainer->m_format + , _flags + , mem + ); } - } - - BX_FREE(allocator, data); - - if (NULL != out) - { - handle = bgfx::createTexture2D( - uint16_t(width) - , uint16_t(height) - , false - , 1 - , format - , _flags - , bgfx::copy(out, width*height*bpp/8) - ); - release(out); if (NULL != _info) { bgfx::calcTextureSize( *_info - , uint16_t(width) - , uint16_t(height) + , uint16_t(imageContainer->m_width) + , uint16_t(imageContainer->m_height) , 0 , false , false , 1 - , format + , imageContainer->m_format ); } } } - else - { - DBG("Failed to load %s.", _filePath); - } return handle; } @@ -473,6 +222,14 @@ bgfx::TextureHandle loadTexture(const char* _name, uint32_t _flags, uint8_t _ski return loadTexture(entry::getFileReader(), _name, _flags, _skip, _info); } +bgfx::ImageContainer* imageLoad(const char* _filePath, bgfx::TextureFormat::Enum _dstFormat) +{ + uint32_t size = 0; + void* data = loadMem(entry::getFileReader(), entry::getAllocator(), _filePath, &size); + + return bgfx::imageParse(entry::getAllocator(), data, size, _dstFormat); +} + void calcTangents(void* _vertices, uint16_t _numVertices, bgfx::VertexDecl _decl, const uint16_t* _indices, uint32_t _numIndices) { struct PosTexcoord diff --git a/examples/common/bgfx_utils.h b/examples/common/bgfx_utils.h index b6731fc22..f948d1bfa 100644 --- a/examples/common/bgfx_utils.h +++ b/examples/common/bgfx_utils.h @@ -7,12 +7,27 @@ #define BGFX_UTILS_H_HEADER_GUARD #include +#include "image.h" +/// void* load(const char* _filePath, uint32_t* _size = NULL); + +/// void unload(void* _ptr); + +/// bgfx::ShaderHandle loadShader(const char* _name); + +/// bgfx::ProgramHandle loadProgram(const char* _vsName, const char* _fsName); + +/// bgfx::TextureHandle loadTexture(const char* _name, uint32_t _flags = BGFX_TEXTURE_NONE, uint8_t _skip = 0, bgfx::TextureInfo* _info = NULL); + +/// +bgfx::ImageContainer* imageLoad(const char* _filePath, bgfx::TextureFormat::Enum _dstFormat); + +/// void calcTangents(void* _vertices, uint16_t _numVertices, bgfx::VertexDecl _decl, const uint16_t* _indices, uint32_t _numIndices); /// Returns true if both internal transient index and vertex buffer have @@ -29,6 +44,7 @@ inline bool checkAvailTransientBuffers(uint32_t _numVertices, const bgfx::Vertex ; } +/// struct MeshState { struct Texture @@ -48,15 +64,25 @@ struct MeshState struct Mesh; +/// Mesh* meshLoad(const char* _filePath); + +/// void meshUnload(Mesh* _mesh); +/// MeshState* meshStateCreate(); + +/// void meshStateDestroy(MeshState* _meshState); +/// void meshSubmit(const Mesh* _mesh, uint8_t _id, bgfx::ProgramHandle _program, const float* _mtx, uint64_t _state = BGFX_STATE_MASK); + +/// void meshSubmit(const Mesh* _mesh, const MeshState*const* _state, uint8_t _numPasses, const float* _mtx, uint16_t _numMatrices = 1); +/// struct Args { Args(int _argc, char** _argv); diff --git a/examples/common/image.cpp b/examples/common/image.cpp new file mode 100644 index 000000000..f0c24977a --- /dev/null +++ b/examples/common/image.cpp @@ -0,0 +1,394 @@ +/* + * Copyright 2011-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause + */ + +#include "entry/dbg.h" + +#include +#include +#include +#include +#include "bgfx_utils.h" + +BX_PRAGMA_DIAGNOSTIC_PUSH() +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wtype-limits") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-parameter") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-value") +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: '' : unreferenced formal parameter +#if BX_PLATFORM_EMSCRIPTEN +# include +#endif // BX_PLATFORM_EMSCRIPTEN +#define MINIZ_NO_STDIO +#define TINYEXR_IMPLEMENTATION +#include +BX_PRAGMA_DIAGNOSTIC_POP() + +#define LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_NO_COMPILE_DISK +#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_NO_COMPILE_CPP +#include + +typedef unsigned char stbi_uc; +extern "C" stbi_uc* stbi_load_from_memory(stbi_uc const* _buffer, int _len, int* _x, int* _y, int* _comp, int _req_comp); +extern "C" void stbi_image_free(void* _ptr); +extern void lodepng_free(void* _ptr); + +namespace bgfx +{ + struct ImageMip + { + TextureFormat::Enum m_format; + uint32_t m_width; + uint32_t m_height; + uint32_t m_blockSize; + uint32_t m_size; + uint8_t m_bpp; + bool m_hasAlpha; + const uint8_t* m_data; + }; + + uint32_t imageGetSize( + TextureInfo* _info + , uint16_t _width + , uint16_t _height + , uint16_t _depth + , bool _cubeMap + , bool _hasMips + , uint16_t _numLayers + , TextureFormat::Enum _format + ); + + /// + ImageContainer* imageParseBgfx(bx::AllocatorI* _allocator, const void* _src, uint32_t _size); + + /// + bool imageConvert( + void* _dst + , TextureFormat::Enum _dstFormat + , const void* _src + , TextureFormat::Enum _srcFormat + , uint32_t _width + , uint32_t _height + ); + + /// + ImageContainer* imageConvert( + bx::AllocatorI* _allocator + , TextureFormat::Enum _dstFormat + , const ImageContainer& _input + ); + +} // namespace bgfx + +namespace bgfx +{ + static ImageContainer* imageParseLodePng(bx::AllocatorI* _allocator, const void* _data, uint32_t _size) + { + static uint8_t pngMagic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0d, 0x0a }; + + if (0 == bx::memCmp(_data, pngMagic, sizeof(pngMagic) ) ) + { + return NULL; + } + + bgfx::TextureFormat::Enum format = bgfx::TextureFormat::RGBA8; + uint32_t width = 0; + uint32_t height = 0; + + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.decoder.color_convert = 0; + + uint8_t* data = NULL; + error = lodepng_decode(&data, &width, &height, &state, (uint8_t*)_data, _size); + + if (0 == error) + { + switch (state.info_raw.bitdepth) + { + case 8: + switch (state.info_raw.colortype) + { + case LCT_GREY: + format = bgfx::TextureFormat::R8; + break; + + case LCT_GREY_ALPHA: + format = bgfx::TextureFormat::RG8; + break; + + case LCT_RGB: + format = bgfx::TextureFormat::RGB8; + break; + + case LCT_RGBA: + format = bgfx::TextureFormat::RGBA8; + break; + + case LCT_PALETTE: + break; + } + break; + + case 16: + switch (state.info_raw.colortype) + { + case LCT_GREY: + for (uint32_t ii = 0, num = width*height; ii < num; ++ii) + { + uint16_t* rgba = (uint16_t*)data + ii; + rgba[0] = bx::toHostEndian(rgba[0], false); + } + format = bgfx::TextureFormat::R16; + break; + + case LCT_GREY_ALPHA: + for (uint32_t ii = 0, num = width*height; ii < num; ++ii) + { + uint16_t* rgba = (uint16_t*)data + ii*2; + rgba[0] = bx::toHostEndian(rgba[0], false); + rgba[1] = bx::toHostEndian(rgba[1], false); + } + format = bgfx::TextureFormat::RG16; + break; + + case LCT_RGBA: + for (uint32_t ii = 0, num = width*height; ii < num; ++ii) + { + uint16_t* rgba = (uint16_t*)data + ii*4; + rgba[0] = bx::toHostEndian(rgba[0], false); + rgba[1] = bx::toHostEndian(rgba[1], false); + rgba[2] = bx::toHostEndian(rgba[2], false); + rgba[3] = bx::toHostEndian(rgba[3], false); + } + format = bgfx::TextureFormat::RGBA16; + break; + + case LCT_RGB: + case LCT_PALETTE: + break; + } + break; + + default: + break; + } + } + + lodepng_state_cleanup(&state); + + ImageContainer* output = imageAlloc(_allocator + , format + , uint16_t(width) + , uint16_t(height) + , 0 + , 1 + , false + , false + , data + ); + lodepng_free(data); + + return output; + } + + static ImageContainer* imageParseTinyExr(bx::AllocatorI* _allocator, const void* _data, uint32_t _size) + { + EXRVersion exrVersion; + int result = ParseEXRVersionFromMemory(&exrVersion, (uint8_t*)_data, _size); + if (TINYEXR_SUCCESS != result) + { + return NULL; + } + + bgfx::TextureFormat::Enum format = bgfx::TextureFormat::RGBA8; + uint32_t width = 0; + uint32_t height = 0; + + uint8_t* data = NULL; + const char* err = NULL; + EXRHeader exrHeader; + result = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, (uint8_t*)_data, _size, &err); + if (TINYEXR_SUCCESS == result) + { + EXRImage exrImage; + InitEXRImage(&exrImage); + + result = LoadEXRImageFromMemory(&exrImage, &exrHeader, (uint8_t*)_data, _size, &err); + if (TINYEXR_SUCCESS == result) + { + uint8_t idxR = UINT8_MAX; + uint8_t idxG = UINT8_MAX; + uint8_t idxB = UINT8_MAX; + uint8_t idxA = UINT8_MAX; + for (uint8_t ii = 0, num = uint8_t(exrHeader.num_channels); ii < num; ++ii) + { + const EXRChannelInfo& channel = exrHeader.channels[ii]; + if (UINT8_MAX == idxR + && 0 == bx::strncmp(channel.name, "R") ) + { + idxR = ii; + } + else if (UINT8_MAX == idxG + && 0 == bx::strncmp(channel.name, "G") ) + { + idxG = ii; + } + else if (UINT8_MAX == idxB + && 0 == bx::strncmp(channel.name, "B") ) + { + idxB = ii; + } + else if (UINT8_MAX == idxA + && 0 == bx::strncmp(channel.name, "A") ) + { + idxA = ii; + } + } + + if (UINT8_MAX != idxR) + { + const bool asFloat = exrHeader.pixel_types[idxR] == TINYEXR_PIXELTYPE_FLOAT; + uint32_t srcBpp = 32; + uint32_t dstBpp = asFloat ? 32 : 16; + format = asFloat ? TextureFormat::R32F : TextureFormat::R16F; + uint32_t stepR = 1; + uint32_t stepG = 0; + uint32_t stepB = 0; + uint32_t stepA = 0; + + if (UINT8_MAX != idxG) + { + srcBpp += 32; + dstBpp = asFloat ? 64 : 32; + format = asFloat ? TextureFormat::RG32F : TextureFormat::RG16F; + stepG = 1; + } + + if (UINT8_MAX != idxB) + { + srcBpp += 32; + dstBpp = asFloat ? 128 : 64; + format = asFloat ? TextureFormat::RGBA32F : TextureFormat::RGBA16F; + stepB = 1; + } + + if (UINT8_MAX != idxA) + { + srcBpp += 32; + dstBpp = asFloat ? 128 : 64; + format = asFloat ? TextureFormat::RGBA32F : TextureFormat::RGBA16F; + stepA = 1; + } + + data = (uint8_t*)BX_ALLOC(_allocator, exrImage.width * exrImage.height * dstBpp/8); + + const float zero = 0.0f; + const float* srcR = UINT8_MAX == idxR ? &zero : (const float*)(exrImage.images)[idxR]; + const float* srcG = UINT8_MAX == idxG ? &zero : (const float*)(exrImage.images)[idxG]; + const float* srcB = UINT8_MAX == idxB ? &zero : (const float*)(exrImage.images)[idxB]; + const float* srcA = UINT8_MAX == idxA ? &zero : (const float*)(exrImage.images)[idxA]; + + const uint32_t bytesPerPixel = dstBpp/8; + for (uint32_t ii = 0, num = exrImage.width * exrImage.height; ii < num; ++ii) + { + float rgba[4] = + { + *srcR, + *srcG, + *srcB, + *srcA, + }; + bx::memCopy(&data[ii * bytesPerPixel], rgba, bytesPerPixel); + + srcR += stepR; + srcG += stepG; + srcB += stepB; + srcA += stepA; + } + } + + FreeEXRImage(&exrImage); + } + + FreeEXRHeader(&exrHeader); + } + + ImageContainer* output = imageAlloc(_allocator + , format + , uint16_t(width) + , uint16_t(height) + , 0 + , 1 + , false + , false + , data + ); + BX_FREE(_allocator, data); + + return output; + } + + static ImageContainer* imageParseStbImage(bx::AllocatorI* _allocator, const void* _data, uint32_t _size) + { + TextureFormat::Enum format = TextureFormat::RGBA8; + uint32_t width = 0; + uint32_t height = 0; + + int comp = 0; + void* data = stbi_load_from_memory( (uint8_t*)_data, _size, (int*)&width, (int*)&height, &comp, 4); + + if (NULL != data) + { + return NULL; + } + + ImageContainer* output = imageAlloc(_allocator + , format + , uint16_t(width) + , uint16_t(height) + , 0 + , 1 + , false + , false + , data + ); + stbi_image_free(data); + + return output; + } + + ImageContainer* imageParse(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, TextureFormat::Enum _dstFormat) + { + ImageContainer* input = imageParseBgfx (_allocator, _data, _size) ; + input = NULL == input ? imageParseLodePng (_allocator, _data, _size) : input; + input = NULL == input ? imageParseTinyExr (_allocator, _data, _size) : input; + input = NULL == input ? imageParseStbImage(_allocator, _data, _size) : input; + + if (NULL == input) + { + return NULL; + } + + _dstFormat = TextureFormat::Count == _dstFormat + ? input->m_format + : _dstFormat + ; + + if (_dstFormat == input->m_format) + { + return input; + } + + ImageContainer* output = imageConvert(_allocator, _dstFormat, *input); + imageFree(input); + + return output; + } + +} // namespace bgfx diff --git a/examples/common/image.h b/examples/common/image.h new file mode 100644 index 000000000..5c8ac1468 --- /dev/null +++ b/examples/common/image.h @@ -0,0 +1,62 @@ +/* + * Copyright 2011-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause + */ + +#ifndef IMAGE_H_HEADER_GUARD +#define IMAGE_H_HEADER_GUARD + +namespace bgfx +{ + /// + struct ImageContainer + { + bx::AllocatorI* m_allocator; + void* m_data; + + TextureFormat::Enum m_format; + + uint32_t m_size; + uint32_t m_offset; + uint32_t m_width; + uint32_t m_height; + uint32_t m_depth; + uint16_t m_numLayers; + uint8_t m_numMips; + bool m_hasAlpha; + bool m_cubeMap; + bool m_ktx; + bool m_ktxLE; + bool m_srgb; + }; + + /// + ImageContainer* imageParse( + bx::AllocatorI* _allocator + , const void* _data + , uint32_t _size + , TextureFormat::Enum _dstFormat = TextureFormat::Count + ); + + /// + ImageContainer* imageAlloc( + bx::AllocatorI* _allocator + , TextureFormat::Enum _format + , uint16_t _width + , uint16_t _height + , uint16_t _depth + , uint16_t _numLayers + , bool _cubeMap + , bool _hasMips + , const void* _data = NULL + ); + + /// + void imageFree(ImageContainer* _imageContainer); + + /// Converts format to string. + const char* getName(TextureFormat::Enum _format); + +} // namespace bgfx + +#endif // IMAGE_H_HEADER_GUARD diff --git a/examples/common/ps/particle_system.cpp b/examples/common/ps/particle_system.cpp index dd09962f3..34ea02619 100644 --- a/examples/common/ps/particle_system.cpp +++ b/examples/common/ps/particle_system.cpp @@ -8,6 +8,7 @@ #include "particle_system.h" #include "../bgfx_utils.h" +#include "../packrect.h" #include #include @@ -183,6 +184,49 @@ namespace ps ; } +#define SPRITE_TEXTURE_SIZE 1024 + template + struct SpriteT + { + SpriteT() + : m_ra(TextureSizeT, TextureSizeT) + { + } + + EmitterSpriteHandle create(uint16_t _width, uint16_t _height) + { + EmitterSpriteHandle handle = { bx::HandleAlloc::invalid }; + + if (m_handleAlloc.getNumHandles() < m_handleAlloc.getMaxHandles() ) + { + Pack2D pack; + if (m_ra.find(_width, _height, pack) ) + { + handle.idx = m_handleAlloc.alloc(); + m_pack[handle.idx] = pack; + } + } + + return handle; + } + + void destroy(EmitterSpriteHandle _sprite) + { + const Pack2D& pack = m_pack[_sprite.idx]; + m_ra.clear(pack); + m_handleAlloc.free(_sprite.idx); + } + + const Pack2D& get(EmitterSpriteHandle _sprite) const + { + return m_pack[_sprite.idx]; + } + + bx::HandleAllocT m_handleAlloc; + Pack2D m_pack[MaxHandlesT]; + RectPack2DT<256> m_ra; + }; + struct Emitter { void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles); @@ -322,7 +366,7 @@ namespace ps } } - uint32_t render(const float* _mtxView, const float* _eye, uint32_t _first, uint32_t _max, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices) + uint32_t render(const float _uv[4], const float* _mtxView, const float* _eye, uint32_t _first, uint32_t _max, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices) { bx::EaseFn easeRgba = s_easeFunc[m_uniforms.m_easeRgba]; bx::EaseFn easePos = s_easeFunc[m_uniforms.m_easePos]; @@ -385,8 +429,8 @@ namespace ps bx::vec3Sub(&vertex->m_x, tmp, vdir); aabbExpand(aabb, &vertex->m_x); vertex->m_abgr = abgr; - vertex->m_u = 0.0f; - vertex->m_v = 0.0f; + vertex->m_u = _uv[0]; + vertex->m_v = _uv[1]; vertex->m_blend = blend; ++vertex; @@ -394,8 +438,8 @@ namespace ps bx::vec3Sub(&vertex->m_x, tmp, vdir); aabbExpand(aabb, &vertex->m_x); vertex->m_abgr = abgr; - vertex->m_u = 1.0f; - vertex->m_v = 0.0f; + vertex->m_u = _uv[2]; + vertex->m_v = _uv[1]; vertex->m_blend = blend; ++vertex; @@ -403,8 +447,8 @@ namespace ps bx::vec3Add(&vertex->m_x, tmp, vdir); aabbExpand(aabb, &vertex->m_x); vertex->m_abgr = abgr; - vertex->m_u = 1.0f; - vertex->m_v = 1.0f; + vertex->m_u = _uv[2]; + vertex->m_v = _uv[3]; vertex->m_blend = blend; ++vertex; @@ -412,8 +456,8 @@ namespace ps bx::vec3Add(&vertex->m_x, tmp, vdir); aabbExpand(aabb, &vertex->m_x); vertex->m_abgr = abgr; - vertex->m_u = 0.0f; - vertex->m_v = 1.0f; + vertex->m_u = _uv[0]; + vertex->m_v = _uv[3]; vertex->m_blend = blend; ++vertex; } @@ -466,7 +510,13 @@ namespace ps m_num = 0; s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Int1); - m_particleTexture = loadTexture("textures/particle.ktx"); + m_texture = bgfx::createTexture2D( + SPRITE_TEXTURE_SIZE + , SPRITE_TEXTURE_SIZE + , false + , 1 + , bgfx::TextureFormat::BGRA8 + ); bgfx::RendererType::Enum type = bgfx::getRendererType(); m_particleProgram = bgfx::createProgram( @@ -479,7 +529,7 @@ namespace ps void shutdown() { bgfx::destroyProgram(m_particleProgram); - bgfx::destroyTexture(m_particleTexture); + bgfx::destroyTexture(m_texture); bgfx::destroyUniform(s_texColor); bx::destroyHandleAlloc(m_allocator, m_emitterAlloc); @@ -488,6 +538,33 @@ namespace ps m_allocator = NULL; } + EmitterSpriteHandle createSprite(uint16_t _width, uint16_t _height, const void* _data) + { + EmitterSpriteHandle handle = m_sprite.create(_width, _height); + + if (isValid(handle) ) + { + const Pack2D& pack = m_sprite.get(handle); + bgfx::updateTexture2D( + m_texture + , 0 + , 0 + , pack.m_x + , pack.m_y + , pack.m_width + , pack.m_height + , bgfx::copy(_data, pack.m_width*pack.m_height*4) + ); + } + + return handle; + } + + void destroy(EmitterSpriteHandle _handle) + { + m_sprite.destroy(_handle); + } + void update(float _dt) { uint32_t numParticles = 0; @@ -535,7 +612,18 @@ namespace ps { const uint16_t idx = m_emitterAlloc->getHandleAt(ii); Emitter& emitter = m_emitter[idx]; - pos += emitter.render(_mtxView, _eye, pos, max, particleSort, vertices); + + const Pack2D& pack = m_sprite.get(emitter.m_uniforms.m_handle); + const float invTextureSize = 1.0f/SPRITE_TEXTURE_SIZE; + const float uv[4] = + { + pack.m_x * invTextureSize, + pack.m_y * invTextureSize, + (pack.m_x + pack.m_width ) * invTextureSize, + (pack.m_y + pack.m_height) * invTextureSize, + }; + + pos += emitter.render(uv, _mtxView, _eye, pos, max, particleSort, vertices); } qsort(particleSort @@ -569,7 +657,7 @@ namespace ps ); bgfx::setVertexBuffer(&tvb); bgfx::setIndexBuffer(&tib); - bgfx::setTexture(0, s_texColor, m_particleTexture); + bgfx::setTexture(0, s_texColor, m_texture); bgfx::submit(_view, m_particleProgram); } } @@ -631,8 +719,11 @@ namespace ps bx::HandleAlloc* m_emitterAlloc; Emitter* m_emitter; + typedef SpriteT<256, SPRITE_TEXTURE_SIZE> Sprite; + Sprite m_sprite; + bgfx::UniformHandle s_texColor; - bgfx::TextureHandle m_particleTexture; + bgfx::TextureHandle m_texture; bgfx::ProgramHandle m_particleProgram; uint32_t m_num; @@ -672,6 +763,16 @@ void psShutdown() s_ctx.shutdown(); } +EmitterSpriteHandle psCreateSprite(uint16_t _width, uint16_t _height, const void* _data) +{ + return s_ctx.createSprite(_width, _height, _data); +} + +void psDestroy(EmitterSpriteHandle _handle) +{ + s_ctx.destroy(_handle); +} + EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles) { return s_ctx.createEmitter(_shape, _direction, _maxParticles); diff --git a/examples/common/ps/particle_system.h b/examples/common/ps/particle_system.h index be7065824..52b7b60e7 100644 --- a/examples/common/ps/particle_system.h +++ b/examples/common/ps/particle_system.h @@ -12,6 +12,15 @@ #include "../bounds.h" +struct EmitterHandle { uint16_t idx; }; +struct EmitterSpriteHandle { uint16_t idx; }; + +template +inline bool isValid(Ty _handle) +{ + return _handle.idx != UINT16_MAX; +} + struct EmitterShape { enum Enum @@ -60,9 +69,9 @@ struct EmitterUniforms bx::Easing::Enum m_easeRgba; bx::Easing::Enum m_easeBlend; bx::Easing::Enum m_easeScale; -}; -struct EmitterHandle { uint16_t idx; }; + EmitterSpriteHandle m_handle; +}; /// void psInit(uint16_t _maxEmitters = 64, bx::AllocatorI* _allocator = NULL); @@ -70,6 +79,12 @@ void psInit(uint16_t _maxEmitters = 64, bx::AllocatorI* _allocator = NULL); /// void psShutdown(); +/// +EmitterSpriteHandle psCreateSprite(uint16_t _width, uint16_t _height, const void* _data); + +/// +void psDestroy(EmitterSpriteHandle _handle); + /// EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles); diff --git a/scripts/texturev.lua b/scripts/texturev.lua index 02103739d..fe08f59d6 100644 --- a/scripts/texturev.lua +++ b/scripts/texturev.lua @@ -11,7 +11,6 @@ project ("texturev") path.join(BGFX_DIR, "examples/common"), path.join(MODULE_DIR, "include"), path.join(MODULE_DIR, "3rdparty"), - path.join(MODULE_DIR, "src"), } files { diff --git a/src/image.cpp b/src/image.cpp index df1f36a3e..ddf43faf8 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -858,7 +858,7 @@ namespace bgfx , 1 < _input.m_numMips ); - const uint8_t bpp = getBitsPerPixel(_dstFormat); + const uint8_t bpp = getBitsPerPixel(_dstFormat); const uint16_t numSides = _input.m_numLayers * (_input.m_cubeMap ? 6 : 1); uint8_t* dst = (uint8_t*)output->m_data ; @@ -906,7 +906,7 @@ namespace bgfx , imageContainer.m_numLayers , imageContainer.m_cubeMap , 1 < imageContainer.m_numMips - , _src + , (uint8_t*)_src + imageContainer.m_offset ); return output; @@ -1712,10 +1712,10 @@ namespace bgfx const uint8_t numMips = _hasMips ? imageGetNumMips(_format, _width, _height) : 1; uint32_t size = imageGetSize(NULL, _width, _height, _depth, _cubeMap, _hasMips, _numLayers, _format); - ImageContainer* imageContainer = (ImageContainer*)BX_ALLOC(_allocator, (NULL != _data ? 0 : size) + sizeof(ImageContainer) ); + ImageContainer* imageContainer = (ImageContainer*)BX_ALLOC(_allocator, size + sizeof(ImageContainer) ); imageContainer->m_allocator = _allocator; - imageContainer->m_data = NULL != _data ? const_cast(_data) : imageContainer + 1; + imageContainer->m_data = imageContainer + 1; imageContainer->m_format = _format; imageContainer->m_size = size; imageContainer->m_offset = 0; @@ -1730,6 +1730,11 @@ namespace bgfx imageContainer->m_ktxLE = false; imageContainer->m_srgb = false; + if (NULL != _data) + { + bx::memCopy(imageContainer->m_data, _data, imageContainer->m_size); + } + return imageContainer; }