From 2f42a76d526537a2902e835636c1a27ee5d55231 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 18 Aug 2024 02:09:25 +0900 Subject: [PATCH] add texture-util to Tydra (e.g. combine independent textures to create single texture map) --- CMakeLists.txt | 2 + src/tydra/texture-util.cc | 170 ++++++++++++++++++++++++++++++++++++++ src/tydra/texture-util.hh | 44 ++++++++++ 3 files changed, 216 insertions(+) create mode 100644 src/tydra/texture-util.cc create mode 100644 src/tydra/texture-util.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 77859ccc..8f98d75c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,8 @@ if (TINYUSDZ_WITH_TYDRA) ${PROJECT_SOURCE_DIR}/src/tydra/shader-network.hh ${PROJECT_SOURCE_DIR}/src/tydra/render-data.cc ${PROJECT_SOURCE_DIR}/src/tydra/render-data.hh + ${PROJECT_SOURCE_DIR}/src/tydra/texture-util.cc + ${PROJECT_SOURCE_DIR}/src/tydra/texture-util.hh ) endif (TINYUSDZ_WITH_TYDRA) diff --git a/src/tydra/texture-util.cc b/src/tydra/texture-util.cc new file mode 100644 index 00000000..55f8a66c --- /dev/null +++ b/src/tydra/texture-util.cc @@ -0,0 +1,170 @@ +#include "texture-util.hh" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +#endif + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + +// stb_image_resize2 implementation define is in src/image-util.cc + +#include "external/stb_image_resize2.h" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace tinyusdz { +namespace tydra { + +bool BuildOcclusionRoughnessMetallicTexture( + const float occlusionFactor, + const float roughnessFactor, + const float metallicFactor, + const std::vector &occlusionImageData, + const size_t occlusionImageWidth, + const size_t occlusionImageHeight, + const size_t occlusionImageChannels, + const size_t occlusionChannel, + const std::vector &roughnessImageData, + const size_t roughnessImageWidth, + const size_t roughnessImageHeight, + const size_t roughnessImageChannels, + const size_t roughnessChannel, + const std::vector &metallicImageData, + const size_t metallicImageWidth, + const size_t metallicImageHeight, + const size_t metallicImageChannels, + const size_t metallicChannel, + std::vector &dst, // RGBA + size_t &dstWidth, + size_t &dstHeight) +{ + if (occlusionChannel > occlusionImageChannels) { + return false; + } + if (roughnessChannel > roughnessImageChannels) { + return false; + } + if (metallicChannel > metallicImageChannels) { + return false; + } + + size_t maxImageWidth = 1; + size_t maxImageHeight = 1; + if (!occlusionImageData.empty()) { + maxImageWidth = (std::max)(maxImageWidth, occlusionImageWidth); + maxImageHeight = (std::max)(maxImageHeight, occlusionImageHeight); + } + if (!roughnessImageData.empty()) { + maxImageWidth = (std::max)(maxImageWidth, roughnessImageWidth); + maxImageHeight = (std::max)(maxImageHeight, roughnessImageHeight); + } + if (!metallicImageData.empty()) { + maxImageWidth = (std::max)(maxImageWidth, metallicImageWidth); + maxImageHeight = (std::max)(maxImageHeight, metallicImageHeight); + } + + std::vector occlusionBuf; + std::vector roughnessBuf; + std::vector metallicBuf; + + if (!occlusionImageData.empty()) { + if ((maxImageWidth != occlusionImageWidth) || (maxImageHeight != occlusionImageHeight)) { + stbir_pixel_layout layout; + if (occlusionImageChannels == 1) { + layout = STBIR_1CHANNEL; + } else if (occlusionImageChannels == 2) { + layout = STBIR_2CHANNEL; + } else if (occlusionImageChannels == 3) { + layout = STBIR_RGB; + } else { // assume RGBA + layout = STBIR_RGBA; + } + + occlusionBuf.resize(maxImageWidth * maxImageHeight * occlusionImageChannels); + + stbir_resize_uint8_linear(occlusionImageData.data(), int(occlusionImageWidth), int(occlusionImageHeight), 0, occlusionBuf.data(), int(maxImageWidth), int(maxImageHeight), 0, layout); + } + } else { + occlusionBuf = occlusionImageData; + } + + if (!metallicImageData.empty()) { + if ((maxImageWidth != metallicImageWidth) || (maxImageHeight != metallicImageHeight)) { + stbir_pixel_layout layout; + if (metallicImageChannels == 1) { + layout = STBIR_1CHANNEL; + } else if (metallicImageChannels == 2) { + layout = STBIR_2CHANNEL; + } else if (metallicImageChannels == 3) { + layout = STBIR_RGB; + } else { // assume RGBA + layout = STBIR_RGBA; + } + + metallicBuf.resize(maxImageWidth * maxImageHeight * metallicImageChannels); + + stbir_resize_uint8_linear(metallicImageData.data(), int(metallicImageWidth), int(metallicImageHeight), 0, metallicBuf.data(), int(maxImageWidth), int(maxImageHeight), 0, layout); + } else { + metallicBuf = metallicImageData; + } + } + + if (!roughnessImageData.empty()) { + if ((maxImageWidth != roughnessImageWidth) || (maxImageHeight != roughnessImageHeight)) { + stbir_pixel_layout layout; + if (roughnessImageChannels == 1) { + layout = STBIR_1CHANNEL; + } else if (roughnessImageChannels == 2) { + layout = STBIR_2CHANNEL; + } else if (roughnessImageChannels == 3) { + layout = STBIR_RGB; + } else { // assume RGBA + layout = STBIR_RGBA; + } + + roughnessBuf.resize(maxImageWidth * maxImageHeight * roughnessImageChannels); + + stbir_resize_uint8_linear(roughnessImageData.data(), int(roughnessImageWidth), int(roughnessImageHeight), 0, roughnessBuf.data(), int(maxImageWidth), int(maxImageHeight), 0, layout); + } else { + roughnessBuf = roughnessImageData; + } + } + + uint8_t occlusionValue = uint8_t((std::max)((std::min)(255, int(occlusionFactor * 255.0f)), 0)); + uint8_t metallicValue = uint8_t((std::max)((std::min)(255, int(metallicFactor * 255.0f)), 0)); + uint8_t roughnessValue = uint8_t((std::max)((std::min)(255, int(roughnessFactor * 255.0f)), 0)); + + dst.resize(maxImageWidth * maxImageHeight * 3); + + for (size_t i = 0; i < maxImageWidth * maxImageHeight; i++) { + // Use the first component of texel when input is a texture. + uint8_t r = occlusionBuf.size() ? occlusionBuf[i * occlusionImageChannels + occlusionChannel] : occlusionValue; + uint8_t g = roughnessBuf.size() ? roughnessBuf[i * roughnessImageChannels + roughnessChannel] : roughnessValue; + uint8_t b = metallicBuf.size() ? metallicBuf[i * metallicImageChannels + metallicChannel] : metallicValue; + + dst[3 * i + 0] = r; + dst[3 * i + 1] = g; + dst[3 * i + 2] = b; + } + + dstWidth = maxImageWidth; + dstHeight = maxImageHeight; + + return true; +} + +} // namespace tydra +} // namespace tinyusdz diff --git a/src/tydra/texture-util.hh b/src/tydra/texture-util.hh new file mode 100644 index 00000000..b8966b6a --- /dev/null +++ b/src/tydra/texture-util.hh @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache 2.0 +// Copyright 2022 - Present, Light Transport Entertainment, Inc. +// +// Texture utility + +#pragma once + +#include +#include +#include + +namespace tinyusdz { +namespace tydra { + +/// Build glTF's occlusion, metallic and roughness texture +/// r: occlusion +/// g: roughness +/// b: metallic +bool BuildOcclusionRoughnessMetallicTexture( + const float occlusionFactor, + const float roughnessFactor, + const float metallicFactor, + const std::vector &occlusionImageData, + const size_t occlusionImageWidth, + const size_t occlusionImageHeight, + const size_t occlusionImageChannels, + const size_t occlusionChannel, + const std::vector &roughnessImageData, + const size_t roughnessImageWidth, + const size_t roughnessImageHeight, + const size_t roughnessImageChannels, + const size_t roughnessChannel, + const std::vector &metallicImageData, + const size_t metallicImageWidth, + const size_t metallicImageHeight, + const size_t metallicImageChannels, + const size_t metallicChannel, + std::vector &dst, // RGBA + size_t &dstWidth, + size_t &dstHeight); + + +} // namespace tydra +} // namespace tinyusdz