From 615dfd5f52551720bdc42970a63330be43066a9f Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 18 Sep 2024 02:54:33 +0000 Subject: [PATCH] Update code for loading normal map from image Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 127 +++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 5 deletions(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index 95db59a1c..91e685c06 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #ifdef _MSC_VER @@ -130,6 +132,13 @@ class gz::rendering::Ogre2MaterialPrivate return "invalid"; } } + + /// \brief Helper function to convert normal maps to two-component normalized + /// signed 8-bit format, and generate mip maps + /// \param[in/out] _texture Normal map texture + /// \param[in/out] _image Normal map image data to be uploaded to the texture + public: void PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, + Ogre::Image2 &_image); }; using namespace gz; @@ -1215,6 +1224,8 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, root->getRenderSystem()->getTextureGpuManager(); // create the gpu texture + Ogre::uint32 filters = Ogre::TextureFilter::TypeGenerateDefaultMipmaps; + filters |= this->ogreDatablock->suggestFiltersForType(_type); Ogre::uint32 textureFlags = 0; textureFlags |= Ogre::TextureFlags::AutomaticBatching; Ogre::TextureGpu *texture = textureMgr->createOrRetrieveTexture( @@ -1223,7 +1234,7 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, textureFlags | Ogre::TextureFlags::ManualTexture, Ogre::TextureTypes::Type2D, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, - 0u); + filters); // Has to be loaded if (texture->getWidth() == 0) @@ -1236,13 +1247,20 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, texture->setTextureType(Ogre::TextureTypes::Type2D); texture->setNumMipmaps(1u); texture->setResolution(_img->Width(), _img->Height()); - texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); - texture->waitForData(); - // upload raw color image data to gpu texture Ogre::Image2 img; img.loadDynamicImage(&data[0], false, texture); - img.uploadTo(texture, 0, 0); + if (_type == Ogre::PBSM_NORMAL) + { + this->dataPtr->PrepareAndUploadNormalMap(texture, img); + } + else + { + // upload raw color image data to gpu texture + texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); + texture->waitForData(); + img.uploadTo(texture, 0, 0); + } } // Now assign it to the material @@ -1558,3 +1576,102 @@ ShaderParamsPtr Ogre2Material::FragmentShaderParams() { return this->dataPtr->fragmentShaderParams; } + +////////////////////////////////////////////////// +void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, + Ogre::Image2 &_image) +{ + // The code below is adapted from ogre-next v2-3. It replicates the steps that + // ogre does when it loads a normal map from file. + // Here we adapte the code to load a normal map texture from an image in + // memory + // \todo(iche033) See if there is a way to reuse these functions + // from ogre-next without copying the code here. + + // Step 1: convert to two component signed 8 bit format: + // Ogre::PFG_RG8_SNORM format + // see PrepareForNormalMapping::_executeStreaming function in + // OgreMain/src/OgreTextureFilters.cpp + const Ogre::uint8 numMipmaps = _image.getNumMipmaps(); + const Ogre::PixelFormatGpu dstFormat = Ogre::PFG_RG8_SNORM; + const Ogre::uint32 rowAlignment = 4u; + const size_t dstSizeBytes = Ogre::PixelFormatGpuUtils::calculateSizeBytes( + _image.getWidth(), + _image.getHeight(), + _image.getDepth(), + _image.getNumSlices(), + dstFormat, numMipmaps, + rowAlignment ); + void *imgData = OGRE_MALLOC_SIMD( dstSizeBytes, Ogre::MEMCATEGORY_RESOURCE); + for (Ogre::uint8 mip = 0; mipsetPixelFormat(dstFormat); + + // _image.save(_name + ".png", 0, 1); + // std::cerr << "tex format " << texture->getWidth() << " " << texture->getHeight() << ": " << texture->getPixelFormat() << " mip " + // << static_cast(texture->getNumMipmaps()) << std::endl; + //std::cerr << "image format " << _image.getWidth() << " " << _image.getHeight() << ": " << _image.getPixelFormat() << std::endl; + //std::cerr << " Ogre::PFG_RG8_SNORM " << Ogre::PFG_RG8_SNORM << std::endl; + + // Step 2: Set mip map count + // see GenerateHwMipmaps::_executeStreaming function in + // OgreMain/src/OgreTextureFilters.cpp + Ogre::uint8 maxMipmaps = Ogre::PixelFormatGpuUtils::getMaxMipmapCount( + _texture->getWidth(), + _texture->getHeight(), + _texture->getDepth() ); + _texture->setNumMipmaps(maxMipmaps); + + _texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); + _texture->waitForData(); + _image.uploadTo(_texture, 0, 0); + + // Step 3: Generate and copy mip maps + // see GenerateHwMipmaps::_executeSerial function in + // OgreMain/src/OgreTextureFilters.cpp + Ogre::TextureGpuManager *textureManager = _texture->getTextureManager(); + Ogre::TextureGpu *tempTexture = textureManager->createTexture( + "___tempMipmapTexture", + Ogre::GpuPageOutStrategy::Discard, + Ogre::TextureFlags::RenderToTexture | + Ogre::TextureFlags::AllowAutomipmaps | + Ogre::TextureFlags::DiscardableContent, + _texture->getTextureType()); + tempTexture->copyParametersFrom(_texture); + tempTexture->unsafeScheduleTransitionTo(Ogre::GpuResidency::Resident); + Ogre::TextureBox box = _texture->getEmptyBox(0); + _texture->copyTo(tempTexture, box, 0, box, 0); + tempTexture->_autogenerateMipmaps(); + + Ogre::uint8 mipmaps = _texture->getNumMipmaps(); + for (size_t i = 1u; i < mipmaps; ++i) + { + box = _texture->getEmptyBox( i ); + tempTexture->copyTo(_texture, box, i, box, i); + } + textureManager->destroyTexture(tempTexture); + tempTexture = 0; +}