From 615dfd5f52551720bdc42970a63330be43066a9f Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 18 Sep 2024 02:54:33 +0000 Subject: [PATCH 1/6] 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; +} From 9c473572643e247e9fa456915f86feb884ab2c3a Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 18 Sep 2024 19:16:11 +0000 Subject: [PATCH 2/6] Generate mipmaps for albedo texture Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 55 +++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index 91e685c06..ed98a61c1 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -15,6 +15,8 @@ * */ +#include + // Note this include is placed in the src file because // otherwise ogre produces compile errors #ifdef _MSC_VER @@ -135,10 +137,20 @@ class gz::rendering::Ogre2MaterialPrivate /// \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] _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); + + /// \brief Allocate mimaps for the texture. This should be done when the + /// texture's residency status is still OnStorage. + /// \param[in] _texture Input texture to allocate mimaps + public: void AllocateMipmaps(Ogre::TextureGpu *_texture); + + /// \brief Generate mimaps for the texture. This should be done when the + /// texture's residency status is Resident. + /// \param[in] _texture Input texture to generate mimpas + public: void GenerateMipmaps(Ogre::TextureGpu *_texture); }; using namespace gz; @@ -1256,10 +1268,17 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, } else { + + if (_type == Ogre::PBSM_DIFFUSE) + this->dataPtr->AllocateMipmaps(texture); + // upload raw color image data to gpu texture texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); texture->waitForData(); img.uploadTo(texture, 0, 0); + + if (_type == Ogre::PBSM_DIFFUSE) + this->dataPtr->GenerateMipmaps(texture); } } @@ -1583,7 +1602,7 @@ void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, { // 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 + // Here we adapt 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. @@ -1630,27 +1649,33 @@ void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, numMipmaps); _texture->setPixelFormat(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: Allocate mip maps + this->AllocateMipmaps(_texture); - // Step 2: Set mip map count - // see GenerateHwMipmaps::_executeStreaming function in + _texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); + _texture->waitForData(); + _image.uploadTo(_texture, 0, 0); + + // Step 3: Generate mip maps + this->GenerateMipmaps(_texture); +} + +////////////////////////////////////////////////// +void Ogre2MaterialPrivate::AllocateMipmaps(Ogre::TextureGpu *_texture) +{ + // code adpated from 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 +////////////////////////////////////////////////// +void Ogre2MaterialPrivate::GenerateMipmaps(Ogre::TextureGpu *_texture) +{ + // code adpated from GenerateHwMipmaps::_executeSerial function in // OgreMain/src/OgreTextureFilters.cpp Ogre::TextureGpuManager *textureManager = _texture->getTextureManager(); Ogre::TextureGpu *tempTexture = textureManager->createTexture( From 820ef56d62a9a1eac7adcf5b68cad8ba3a2d7732 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 18 Sep 2024 19:22:05 +0000 Subject: [PATCH 3/6] lint Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index ed98a61c1..887e2ad2e 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -1622,7 +1622,7 @@ void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, dstFormat, numMipmaps, rowAlignment ); void *imgData = OGRE_MALLOC_SIMD( dstSizeBytes, Ogre::MEMCATEGORY_RESOURCE); - for (Ogre::uint8 mip = 0; mip Date: Wed, 18 Sep 2024 19:26:28 +0000 Subject: [PATCH 4/6] remove line Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index 887e2ad2e..91cfdb102 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -1268,7 +1268,6 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, } else { - if (_type == Ogre::PBSM_DIFFUSE) this->dataPtr->AllocateMipmaps(texture); From b47009546c748c43869f665146b7ee9d75af5cae Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Thu, 19 Sep 2024 23:34:13 +0000 Subject: [PATCH 5/6] minor refactoring Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 70 +++++++++++++++----------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index 91cfdb102..612401c38 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -135,11 +135,11 @@ class gz::rendering::Ogre2MaterialPrivate } } - /// \brief Helper function to convert normal maps to two-component normalized - /// signed 8-bit format, and generate mip maps + /// \brief Prepare for normal mapping by converting to two-component + /// normalized signed 8-bit format /// \param[in] _texture Normal map texture - /// \param[in/out] _image Normal map image data to be uploaded to the texture - public: void PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, + /// \param[in/out] _image Normal map image data + public: void PrepareForNormalMapping(Ogre::TextureGpu *_texture, Ogre::Image2 &_image); /// \brief Allocate mimaps for the texture. This should be done when the @@ -1262,23 +1262,28 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, Ogre::Image2 img; img.loadDynamicImage(&data[0], false, texture); + + // Replicates the steps that ogre does when it loads a texture map from + // file. For normal maps, it is first converted to a two component signed + // 8 bit format. Albedo and normal maps will have mipmaps generated. + // \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 if (_type == Ogre::PBSM_NORMAL) - { - this->dataPtr->PrepareAndUploadNormalMap(texture, img); - } - else - { - if (_type == Ogre::PBSM_DIFFUSE) - this->dataPtr->AllocateMipmaps(texture); + this->dataPtr->PrepareForNormalMapping(texture, img); - // upload raw color image data to gpu texture - texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); - texture->waitForData(); - img.uploadTo(texture, 0, 0); + if (_type == Ogre::PBSM_DIFFUSE || _type == Ogre::PBSM_NORMAL) + this->dataPtr->AllocateMipmaps(texture); - if (_type == Ogre::PBSM_DIFFUSE) - this->dataPtr->GenerateMipmaps(texture); - } + // Upload raw color image data to gpu texture + texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); + texture->waitForData(); + img.uploadTo(texture, 0, 0); + + if (_type == Ogre::PBSM_DIFFUSE || _type == Ogre::PBSM_NORMAL) + this->dataPtr->GenerateMipmaps(texture); } // Now assign it to the material @@ -1596,20 +1601,11 @@ ShaderParamsPtr Ogre2Material::FragmentShaderParams() } ////////////////////////////////////////////////// -void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, +void Ogre2MaterialPrivate::PrepareForNormalMapping(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 adapt 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 + // code adpated from PrepareForNormalMapping::_executeStreaming function in + // OgreMain/src/OgreTextureFilters.cpp (v2-3) const Ogre::uint8 numMipmaps = _image.getNumMipmaps(); const Ogre::PixelFormatGpu dstFormat = Ogre::PFG_RG8_SNORM; const Ogre::uint32 rowAlignment = 4u; @@ -1647,23 +1643,13 @@ void Ogre2MaterialPrivate::PrepareAndUploadNormalMap(Ogre::TextureGpu *_texture, _image.getDepthOrSlices(), _image.getTextureType(), dstFormat, false, numMipmaps); _texture->setPixelFormat(dstFormat); - - // Step 2: Allocate mip maps - this->AllocateMipmaps(_texture); - - _texture->scheduleTransitionTo(Ogre::GpuResidency::Resident); - _texture->waitForData(); - _image.uploadTo(_texture, 0, 0); - - // Step 3: Generate mip maps - this->GenerateMipmaps(_texture); } ////////////////////////////////////////////////// void Ogre2MaterialPrivate::AllocateMipmaps(Ogre::TextureGpu *_texture) { // code adpated from GenerateHwMipmaps::_executeStreaming function in - // OgreMain/src/OgreTextureFilters.cpp + // OgreMain/src/OgreTextureFilters.cpp (v2-3) Ogre::uint8 maxMipmaps = Ogre::PixelFormatGpuUtils::getMaxMipmapCount( _texture->getWidth(), _texture->getHeight(), @@ -1675,7 +1661,7 @@ void Ogre2MaterialPrivate::AllocateMipmaps(Ogre::TextureGpu *_texture) void Ogre2MaterialPrivate::GenerateMipmaps(Ogre::TextureGpu *_texture) { // code adpated from GenerateHwMipmaps::_executeSerial function in - // OgreMain/src/OgreTextureFilters.cpp + // OgreMain/src/OgreTextureFilters.cpp (v2-3) Ogre::TextureGpuManager *textureManager = _texture->getTextureManager(); Ogre::TextureGpu *tempTexture = textureManager->createTexture( "___tempMipmapTexture", From 4edeb5cbf76a3133ebfc34811a281b0c5fda5f60 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Fri, 20 Sep 2024 00:25:01 +0000 Subject: [PATCH 6/6] update comment Signed-off-by: Ian Chen --- ogre2/src/Ogre2Material.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index a394f6f57..f3009d408 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -1274,7 +1274,7 @@ void Ogre2Material::SetTextureMapDataImpl(const std::string& _name, // \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: + // Normal maps - convert to two component signed 8 bit format: // Ogre::PFG_RG8_SNORM format if (_type == Ogre::PBSM_NORMAL) this->dataPtr->PrepareForNormalMapping(texture, img);