From 95666da0397c9f9459ed2c4d392e91326205df71 Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Wed, 1 Apr 2026 03:51:23 -0700 Subject: [PATCH] Introduce a Texture class that wraps filament::Texture. PiperOrigin-RevId: 892839636 Change-Id: I3c08fba0a514cfb758ec2842d8510c6d88300874 --- .../filament/filament/drawable.cc | 8 +- src/experimental/filament/filament/drawable.h | 3 +- .../filament/filament/material.cc | 85 ++----- src/experimental/filament/filament/material.h | 20 +- .../filament/filament/model_objects.cc | 64 ++--- .../filament/filament/model_objects.h | 11 +- .../filament/filament/object_manager.cc | 62 ++--- .../filament/filament/object_manager.h | 16 +- .../filament/filament/render_target_util.cc | 36 +-- .../filament/filament/render_target_util.h | 12 +- .../filament/filament/texture_util.cc | 239 +++++++++--------- .../filament/filament/texture_util.h | 51 ++-- 12 files changed, 292 insertions(+), 315 deletions(-) diff --git a/src/experimental/filament/filament/drawable.cc b/src/experimental/filament/filament/drawable.cc index 30f1b9f0..55250600 100644 --- a/src/experimental/filament/filament/drawable.cc +++ b/src/experimental/filament/filament/drawable.cc @@ -37,6 +37,7 @@ #include "experimental/filament/filament/math_util.h" #include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/object_manager.h" +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -226,7 +227,7 @@ void Drawable::SetDrawMode(Material::DrawMode mode) { renderables_.SetMaterialInstance(material_.GetMaterialInstance(mode)); } -void Drawable::UpdateReflectionTexture(const filament::Texture* tex) { +void Drawable::UpdateReflectionTexture(const Texture* tex) { material_.UpdateReflectionTexture(tex); } @@ -422,7 +423,7 @@ void Drawable::UpdateMaterial(const mjvGeom& geom, bool use_segid_color, } else { material_.SetNormalMaterialType(ObjectManager::kPhongColor); } - } else if (textures.color->getTarget() == + } else if (textures.color->GetFilamentTexture()->getTarget() == filament::Texture::Sampler::SAMPLER_CUBEMAP) { if (color.a < 1.0f) { material_.SetNormalMaterialType(ObjectManager::kPhongCubeFade); @@ -490,7 +491,8 @@ void Drawable::UpdateMaterial(const mjvGeom& geom, bool use_segid_color, // the programmatic UVs. if (textures.color) { - if (textures.color->getTarget() == filament::Texture::Sampler::SAMPLER_2D) { + if (textures.color->GetFilamentTexture()->getTarget() == + filament::Texture::Sampler::SAMPLER_2D) { // For 2D textures, `tex_repeat` specifies how many times the texture // image is repeated. The `tex_uniform` flag determines if the repetition // is applied at in object space (false) or in world space (true). diff --git a/src/experimental/filament/filament/drawable.h b/src/experimental/filament/filament/drawable.h index 19002de6..06796aed 100644 --- a/src/experimental/filament/filament/drawable.h +++ b/src/experimental/filament/filament/drawable.h @@ -26,6 +26,7 @@ #include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/renderables.h" +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -68,7 +69,7 @@ class Drawable { // Sets the reflection texture for the drawable. We have a separate setter // because we need to render the reflection texture before it can be applied // to the material. - void UpdateReflectionTexture(const filament::Texture* tex); + void UpdateReflectionTexture(const Texture* tex); private: void AddMesh(int data_id); diff --git a/src/experimental/filament/filament/material.cc b/src/experimental/filament/filament/material.cc index 08c167a2..e04e3f81 100644 --- a/src/experimental/filament/filament/material.cc +++ b/src/experimental/filament/filament/material.cc @@ -21,6 +21,7 @@ #include #include #include "experimental/filament/filament/object_manager.h" +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -71,7 +72,7 @@ void Material::UpdateTextures(const Textures& textures) { UpdateMaterialInstances(); } -void Material::UpdateReflectionTexture(const filament::Texture* tex) { +void Material::UpdateReflectionTexture(const Texture* tex) { textures_.reflection = tex; UpdateMaterialInstances(); } @@ -128,70 +129,26 @@ void Material::UpdateMaterialInstances() { sampler.setMinFilter( filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR); - if (material->hasParameter("BaseColor")) { - if (textures_.color) { - instance->setParameter("BaseColor", textures_.color, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_RGB); - instance->setParameter("BaseColor", fallback, sampler); + auto TrySetTexture = [&](const char* name, const Texture* texture, + mjtTextureRole role) { + if (material->hasParameter(name)) { + if (texture) { + instance->setParameter(name, texture->GetFilamentTexture(), sampler); + } else { + auto* fallback = object_mgr_->GetFallbackTexture(role); + instance->setParameter(name, fallback->GetFilamentTexture(), sampler); + } } - } - if (material->hasParameter("Normal")) { - if (textures_.normal) { - instance->setParameter("Normal", textures_.normal, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_NORMAL); - instance->setParameter("Normal", fallback, sampler); - } - } - if (material->hasParameter("Metallic")) { - if (textures_.metallic) { - instance->setParameter("Metallic", textures_.metallic, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_METALLIC); - instance->setParameter("Metallic", fallback, sampler); - } - } - if (material->hasParameter("Roughness")) { - if (textures_.roughness) { - instance->setParameter("Roughness", textures_.roughness, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_ROUGHNESS); - instance->setParameter("Roughness", fallback, sampler); - } - } - if (material->hasParameter("Occlusion")) { - if (textures_.occlusion) { - instance->setParameter("Occlusion", textures_.occlusion, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_OCCLUSION); - instance->setParameter("Occlusion", fallback, sampler); - } - } - if (material->hasParameter("ORM")) { - if (textures_.orm) { - instance->setParameter("ORM", textures_.orm, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_ORM); - instance->setParameter("ORM", fallback, sampler); - } - } - if (material->hasParameter("Emissive")) { - if (textures_.emissive) { - instance->setParameter("Emissive", textures_.emissive, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_EMISSIVE); - instance->setParameter("Emissive", fallback, sampler); - } - } - if (material->hasParameter("Reflection")) { - if (textures_.reflection) { - instance->setParameter("Reflection", textures_.reflection, sampler); - } else { - auto* fallback = object_mgr_->GetFallbackTexture(mjTEXROLE_USER); - instance->setParameter("Reflection", fallback, sampler); - } - } + }; + + TrySetTexture("BaseColor", textures_.color, mjTEXROLE_RGB); + TrySetTexture("Normal", textures_.normal, mjTEXROLE_NORMAL); + TrySetTexture("Metallic", textures_.metallic, mjTEXROLE_METALLIC); + TrySetTexture("Roughness", textures_.roughness, mjTEXROLE_ROUGHNESS); + TrySetTexture("Occlusion", textures_.occlusion, mjTEXROLE_OCCLUSION); + TrySetTexture("ORM", textures_.orm, mjTEXROLE_ORM); + TrySetTexture("Emissive", textures_.emissive, mjTEXROLE_EMISSIVE); + TrySetTexture("Reflection", textures_.reflection, mjTEXROLE_USER); } } // namespace mujoco diff --git a/src/experimental/filament/filament/material.h b/src/experimental/filament/filament/material.h index 51ccac71..849cb89e 100644 --- a/src/experimental/filament/filament/material.h +++ b/src/experimental/filament/filament/material.h @@ -17,11 +17,11 @@ #include #include -#include #include #include #include #include "experimental/filament/filament/object_manager.h" +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -39,14 +39,14 @@ class Material { // The textures that can be assigned to the drawable's material. struct Textures { - const filament::Texture* color = nullptr; - const filament::Texture* normal = nullptr; - const filament::Texture* metallic = nullptr; - const filament::Texture* roughness = nullptr; - const filament::Texture* occlusion = nullptr; - const filament::Texture* orm = nullptr; - const filament::Texture* emissive = nullptr; - const filament::Texture* reflection = nullptr; + const Texture* color = nullptr; + const Texture* normal = nullptr; + const Texture* metallic = nullptr; + const Texture* roughness = nullptr; + const Texture* occlusion = nullptr; + const Texture* orm = nullptr; + const Texture* emissive = nullptr; + const Texture* reflection = nullptr; }; // The parameters that can be applied to the drawable's material. @@ -82,7 +82,7 @@ class Material { // Update the reflection texture. We do this separately since the reflection // texture needs to be rendered before it can be applied to the material. - void UpdateReflectionTexture(const filament::Texture* tex); + void UpdateReflectionTexture(const Texture* tex); // Returns the material instance assigned to the draw mode. filament::MaterialInstance* GetMaterialInstance(DrawMode mode) { diff --git a/src/experimental/filament/filament/model_objects.cc b/src/experimental/filament/filament/model_objects.cc index 217648ae..0529d90e 100644 --- a/src/experimental/filament/filament/model_objects.cc +++ b/src/experimental/filament/filament/model_objects.cc @@ -15,6 +15,7 @@ #include "experimental/filament/filament/model_objects.h" #include +#include #include #include @@ -80,9 +81,7 @@ ModelObjects::~ModelObjects() { engine_->destroy(iter.vertex_buffer); engine_->destroy(iter.index_buffer); } - for (auto& iter : textures_) { - engine_->destroy(iter.second); - } + textures_.clear(); } void ModelObjects::UploadMesh(const mjModel* model, int id) { @@ -126,25 +125,30 @@ void ModelObjects::UploadTexture(const mjModel* model, int id) { mju_error("Invalid texture index: %d", id); } - if (auto iter = textures_.find(id); iter != textures_.end()) { - engine_->destroy(iter->second); - } + const int width = model->tex_width[id]; + const int height = model->tex_height[id]; + const int num_channels = model->tex_nchannel[id]; + const int tex_type = model->tex_type[id]; + const mjtByte* data = model->tex_data + model->tex_adr[id]; + const mjtColorSpace color_space = (mjtColorSpace)model->tex_colorspace[id]; - const int texture_type = model->tex_type[id]; - if (model->tex_height[id] == 1) { - const mjtByte* bytes = model->tex_data + model->tex_adr[id]; - const int num_bytes = model->tex_width[id]; - textures_[id] = - CreateKtxTexture(engine_, bytes, num_bytes, spherical_harmonics_[id]); - } else if (texture_type == mjTEXTURE_2D) { - textures_[id] = CreateTexture(engine_, model, id, TextureType::kNormal2d); - } else if (texture_type == mjTEXTURE_CUBE) { - textures_[id] = CreateTexture(engine_, model, id, TextureType::kCube); - } else if (texture_type == mjTEXTURE_SKYBOX) { - textures_[id] = CreateTexture(engine_, model, id, TextureType::kCube); - } else { - mju_error("Unsupported: Texture type: %d", texture_type); - } + const TextureType type = [&] { + if (height == 1) { + return TextureType::kKtx; + } else if (tex_type == mjTEXTURE_2D) { + return TextureType::kNormal2d; + } else if (tex_type == mjTEXTURE_CUBE) { + return TextureType::kCube; + } else if (tex_type == mjTEXTURE_SKYBOX) { + return TextureType::kCube; + } else { + mju_error("Unsupported texture type: %d", tex_type); + return TextureType::kNormal2d; + } + }(); + + textures_[id] = std::make_unique(engine_, type, color_space, width, + height, num_channels, data); } void ModelObjects::UploadHeightField(const mjModel* model, int id) { @@ -194,12 +198,12 @@ const FilamentBuffers* ModelObjects::GetShapeBuffer(ShapeType shape) const { return &shapes_[shape]; } -const filament::Texture* ModelObjects::GetTexture(int tex_id) const { +const Texture* ModelObjects::GetTexture(int tex_id) const { auto it = textures_.find(tex_id); - return it != textures_.end() ? it->second : nullptr; + return it != textures_.end() ? it->second.get() : nullptr; } -const filament::Texture* ModelObjects::GetTexture(int mat_id, int role) const { +const Texture* ModelObjects::GetTexture(int mat_id, int role) const { if (mat_id < 0 || mat_id >= model_->nmat || role < 0 || role >= mjNTEXROLE) { return nullptr; } @@ -210,15 +214,11 @@ const filament::Texture* ModelObjects::GetTexture(int mat_id, int role) const { filament::IndirectLight* ModelObjects::CreateIndirectLight(int tex_id, float intensity) { filament::Texture* texture = nullptr; + const Texture::SphericalHarmonics* spherical_harmonics = nullptr; auto texture_iter = textures_.find(tex_id); if (texture_iter != textures_.end()) { - texture = texture_iter->second; - } - - SphericalHarmonics* spherical_harmonics = nullptr; - auto sh_iter = spherical_harmonics_.find(tex_id); - if (sh_iter != spherical_harmonics_.end()) { - spherical_harmonics = &sh_iter->second; + texture = texture_iter->second->GetFilamentTexture(); + spherical_harmonics = texture_iter->second->GetSphericalHarmonics(); } filament::IndirectLight::Builder builder; @@ -240,7 +240,7 @@ filament::Skybox* ModelObjects::CreateSkybox() { for (auto& iter : textures_) { const int texture_type = model_->tex_type[iter.first]; if (texture_type == mjTEXTURE_SKYBOX) { - skybox_texture = iter.second; + skybox_texture = iter.second->GetFilamentTexture(); break; } } diff --git a/src/experimental/filament/filament/model_objects.h b/src/experimental/filament/filament/model_objects.h index 5de28f8a..94fc2987 100644 --- a/src/experimental/filament/filament/model_objects.h +++ b/src/experimental/filament/filament/model_objects.h @@ -16,6 +16,7 @@ #define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_MODEL_OBJECTS_H_ #include +#include #include #include @@ -25,6 +26,7 @@ #include #include #include "experimental/filament/filament/buffer_util.h" +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -62,8 +64,8 @@ class ModelObjects { const FilamentBuffers* GetShapeBuffer(ShapeType shape) const; const FilamentBuffers* GetMeshBuffer(int data_id) const; const FilamentBuffers* GetHeightFieldBuffer(int hfield_id) const; - const filament::Texture* GetTexture(int tex_id) const; - const filament::Texture* GetTexture(int mat_id, int role) const; + const Texture* GetTexture(int tex_id) const; + const Texture* GetTexture(int mat_id, int role) const; filament::Skybox* CreateSkybox(); filament::IndirectLight* CreateIndirectLight(int tex_id, float intensity); @@ -78,8 +80,6 @@ class ModelObjects { ModelObjects& operator=(const ModelObjects&) = delete; private: - using SphericalHarmonics = filament::math::float3[9]; - const mjModel* model_ = nullptr; filament::Engine* engine_ = nullptr; std::vector skyboxes_; @@ -88,8 +88,7 @@ class ModelObjects { std::unordered_map meshes_; std::unordered_map convex_hulls_; std::unordered_map height_fields_; - std::unordered_map textures_; - std::unordered_map spherical_harmonics_; + std::unordered_map> textures_; float specular_multiplier_ = 0.2f; float shininess_multiplier_ = 0.1f; float emissive_multiplier_ = 0.3f; diff --git a/src/experimental/filament/filament/object_manager.cc b/src/experimental/filament/filament/object_manager.cc index 4bf5a0cd..7699e655 100644 --- a/src/experimental/filament/filament/object_manager.cc +++ b/src/experimental/filament/filament/object_manager.cc @@ -15,6 +15,7 @@ #include "experimental/filament/filament/object_manager.h" #include +#include #include #include @@ -86,22 +87,27 @@ ObjectManager::ObjectManager(filament::Engine* engine) materials_[kUnlitUi] = LoadMaterial("unlit_ui.filamat"); static uint8_t black_rgb[3] = {0, 0, 0}; - fallback_black_ = Create2dTexture(engine_, 1, 1, 3, black_rgb, false); + fallback_black_ = std::make_unique( + engine_, TextureType::kNormal2d, mjCOLORSPACE_LINEAR, 1, 1, 3, black_rgb); static uint8_t white_rgb[3] = {255, 255, 255}; - fallback_white_ = Create2dTexture(engine_, 1, 1, 3, white_rgb, false); + fallback_white_ = std::make_unique( + engine_, TextureType::kNormal2d, mjCOLORSPACE_LINEAR, 1, 1, 3, white_rgb); static uint8_t normal_data[3] = {128, 128, 255}; - fallback_normal_ = Create2dTexture(engine_, 1, 1, 3, normal_data, false); + fallback_normal_ = + std::make_unique(engine_, TextureType::kNormal2d, + mjCOLORSPACE_LINEAR, 1, 1, 3, normal_data); static uint8_t orm_data[3] = {0, 255, 0}; - fallback_orm_ = Create2dTexture(engine_, 1, 1, 3, orm_data, false); + fallback_orm_ = std::make_unique( + engine_, TextureType::kNormal2d, mjCOLORSPACE_LINEAR, 1, 1, 3, orm_data); - fallback_textures_[mjTEXROLE_USER] = fallback_black_; - fallback_textures_[mjTEXROLE_RGB] = fallback_white_; - fallback_textures_[mjTEXROLE_OCCLUSION] = fallback_white_; - fallback_textures_[mjTEXROLE_ROUGHNESS] = fallback_white_; - fallback_textures_[mjTEXROLE_METALLIC] = fallback_black_; - fallback_textures_[mjTEXROLE_NORMAL] = fallback_normal_; - fallback_textures_[mjTEXROLE_EMISSIVE] = fallback_black_; - fallback_textures_[mjTEXROLE_ORM] = fallback_orm_; + fallback_textures_[mjTEXROLE_USER] = fallback_black_.get(); + fallback_textures_[mjTEXROLE_RGB] = fallback_white_.get(); + fallback_textures_[mjTEXROLE_OCCLUSION] = fallback_white_.get(); + fallback_textures_[mjTEXROLE_ROUGHNESS] = fallback_white_.get(); + fallback_textures_[mjTEXROLE_METALLIC] = fallback_black_.get(); + fallback_textures_[mjTEXROLE_NORMAL] = fallback_normal_.get(); + fallback_textures_[mjTEXROLE_EMISSIVE] = fallback_black_.get(); + fallback_textures_[mjTEXROLE_ORM] = fallback_orm_.get(); LoadFallbackIndirectLight("ibl.ktx", 1.0f); } @@ -110,17 +116,10 @@ ObjectManager::~ObjectManager() { if (fallback_indirect_light_) { engine_->destroy(fallback_indirect_light_); } - if (fallback_indirect_light_texture_) { - engine_->destroy(fallback_indirect_light_texture_); - } + fallback_indirect_light_texture_.reset(); for (auto& iter : materials_) { engine_->destroy(iter); } - // fallback_textures_ maps to these textures. - engine_->destroy(fallback_white_); - engine_->destroy(fallback_black_); - engine_->destroy(fallback_normal_); - engine_->destroy(fallback_orm_); } filament::Material* ObjectManager::GetMaterial(MaterialType type) const { @@ -130,7 +129,7 @@ filament::Material* ObjectManager::GetMaterial(MaterialType type) const { return materials_[type]; } -const filament::Texture* ObjectManager::GetFallbackTexture( +const Texture* ObjectManager::GetFallbackTexture( mjtTextureRole role) const { if (role < 0 || role >= mjNTEXROLE) { mju_error("Invalid texture role: %d", role); @@ -144,10 +143,7 @@ filament::IndirectLight* ObjectManager::GetFallbackIndirectLight() { void ObjectManager::LoadFallbackIndirectLight( std::string_view filename, float intensity) { - if (fallback_indirect_light_texture_ != nullptr) { - engine_->destroy(fallback_indirect_light_texture_); - fallback_indirect_light_texture_ = nullptr; - } + fallback_indirect_light_texture_.reset(); if (fallback_indirect_light_ != nullptr) { engine_->destroy(fallback_indirect_light_); fallback_indirect_light_ = nullptr; @@ -158,18 +154,22 @@ void ObjectManager::LoadFallbackIndirectLight( return; } - filament::math::float3 spherical_harmonics[9]; - fallback_indirect_light_texture_ = - CreateKtxTexture(engine_, reinterpret_cast(asset.payload), - asset.size, spherical_harmonics); + fallback_indirect_light_texture_ = std::make_unique( + engine_, TextureType::kKtx, mjCOLORSPACE_AUTO, asset.size, 1, 1, + reinterpret_cast(asset.payload)); if (fallback_indirect_light_texture_ == nullptr) { return; } + const Texture::SphericalHarmonics* spherical_harmonics = + fallback_indirect_light_texture_->GetSphericalHarmonics(); + // Build the indirect light. filament::IndirectLight::Builder builder; - builder.reflections(fallback_indirect_light_texture_); - builder.irradiance(3, spherical_harmonics); + builder.reflections(fallback_indirect_light_texture_->GetFilamentTexture()); + if (spherical_harmonics) { + builder.irradiance(3, *spherical_harmonics); + } builder.intensity(intensity); // Rotate the light to match mujoco's Z-up convention. builder.rotation(filament::math::mat3f::rotation( diff --git a/src/experimental/filament/filament/object_manager.h b/src/experimental/filament/filament/object_manager.h index 37459b4d..64d9eadf 100644 --- a/src/experimental/filament/filament/object_manager.h +++ b/src/experimental/filament/filament/object_manager.h @@ -16,12 +16,14 @@ #define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_OBJECT_MANAGER_H_ #include +#include #include #include #include #include #include +#include "experimental/filament/filament/texture_util.h" namespace mujoco { @@ -60,7 +62,7 @@ class ObjectManager { filament::Material* GetMaterial(MaterialType type) const; // Returns the fallback Texture with the given role. - const filament::Texture* GetFallbackTexture(mjtTextureRole role) const; + const Texture* GetFallbackTexture(mjtTextureRole role) const; // Returns the fallback IndirectLight. filament::IndirectLight* GetFallbackIndirectLight(); @@ -74,12 +76,12 @@ class ObjectManager { private: filament::Engine* engine_ = nullptr; std::array materials_; - std::array fallback_textures_; - filament::Texture* fallback_white_ = nullptr; - filament::Texture* fallback_black_ = nullptr; - filament::Texture* fallback_normal_ = nullptr; - filament::Texture* fallback_orm_ = nullptr; - filament::Texture* fallback_indirect_light_texture_ = nullptr; + std::array fallback_textures_; + std::unique_ptr fallback_white_ = nullptr; + std::unique_ptr fallback_black_ = nullptr; + std::unique_ptr fallback_normal_ = nullptr; + std::unique_ptr fallback_orm_ = nullptr; + std::unique_ptr fallback_indirect_light_texture_ = nullptr; filament::IndirectLight* fallback_indirect_light_ = nullptr; }; diff --git a/src/experimental/filament/filament/render_target_util.cc b/src/experimental/filament/filament/render_target_util.cc index 306bb258..ef221e4e 100644 --- a/src/experimental/filament/filament/render_target_util.cc +++ b/src/experimental/filament/filament/render_target_util.cc @@ -14,15 +14,15 @@ #include "experimental/filament/filament/render_target_util.h" +#include + #include #include #include -#include +#include "experimental/filament/filament/texture_util.h" namespace mujoco { - - RenderTargetAndTextures::RenderTargetAndTextures(filament::Engine* engine, RenderTargetTextureType color, RenderTargetTextureType depth) @@ -41,15 +41,15 @@ void RenderTargetAndTextures::Prepare(int width, int height) { height_ = height; color_texture_ = - CreateRenderTargetTexture(engine_, width, height, color_type_); + std::make_unique(engine_, color_type_, width, height); depth_texture_ = - CreateRenderTargetTexture(engine_, width, height, depth_type_); + std::make_unique(engine_, depth_type_, width, height); filament::RenderTarget::Builder builder; builder.texture(filament::RenderTarget::AttachmentPoint::COLOR, - color_texture_); + color_texture_->GetFilamentTexture()); builder.texture(filament::RenderTarget::AttachmentPoint::DEPTH, - depth_texture_); + depth_texture_->GetFilamentTexture()); render_target_ = builder.build(*engine_); } @@ -58,14 +58,20 @@ void RenderTargetAndTextures::Destroy() { engine_->destroy(render_target_); render_target_ = nullptr; } - if (color_texture_) { - engine_->destroy(color_texture_); - color_texture_ = nullptr; - } - if (depth_texture_) { - engine_->destroy(depth_texture_); - depth_texture_ = nullptr; - } + color_texture_.reset(); + depth_texture_.reset(); +} + +Texture* RenderTargetAndTextures::GetColorTexture() const { + return color_texture_.get(); +} + +Texture* RenderTargetAndTextures::GetDepthTexture() const { + return depth_texture_.get(); +} + +filament::RenderTarget* RenderTargetAndTextures::GetRenderTarget() const { + return render_target_; } } // namespace mujoco diff --git a/src/experimental/filament/filament/render_target_util.h b/src/experimental/filament/filament/render_target_util.h index c7651741..d2d581a9 100644 --- a/src/experimental/filament/filament/render_target_util.h +++ b/src/experimental/filament/filament/render_target_util.h @@ -15,6 +15,8 @@ #ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_RENDER_TARGET_UTIL_H_ #define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_RENDER_TARGET_UTIL_H_ +#include + #include #include #include "experimental/filament/filament/texture_util.h" @@ -39,21 +41,21 @@ class RenderTargetAndTextures { void Prepare(int width, int height); // Returns the color texture. - filament::Texture* GetColorTexture() const { return color_texture_; } + Texture* GetColorTexture() const; // Returns the depth texture. - filament::Texture* GetDepthTexture() const { return depth_texture_; } + Texture* GetDepthTexture() const; // Returns the render target. - filament::RenderTarget* GetRenderTarget() const { return render_target_; } + filament::RenderTarget* GetRenderTarget() const; private: void Destroy(); filament::Engine* engine_ = nullptr; - filament::Texture* color_texture_ = nullptr; - filament::Texture* depth_texture_ = nullptr; filament::RenderTarget* render_target_ = nullptr; + std::unique_ptr color_texture_ = nullptr; + std::unique_ptr depth_texture_ = nullptr; RenderTargetTextureType color_type_; RenderTargetTextureType depth_type_; int width_ = 0; diff --git a/src/experimental/filament/filament/texture_util.cc b/src/experimental/filament/filament/texture_util.cc index c003f403..20b63393 100644 --- a/src/experimental/filament/filament/texture_util.cc +++ b/src/experimental/filament/filament/texture_util.cc @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace mujoco { @@ -69,107 +68,24 @@ static filament::Texture::InternalFormat GetTextureInternalFormat( } } -filament::Texture* Create2dTexture(filament::Engine* engine, int width, - int height, int num_channels, - const uint8_t* data, bool is_srgb) { - if (num_channels != 1 && num_channels != 3 && num_channels != 4) { - mju_error("Unsupported number of channels: %d", num_channels); - return nullptr; - } - - filament::Texture::Builder builder; - builder.width(width); - builder.height(height); - builder.format(GetTextureInternalFormat(num_channels, is_srgb)); - builder.sampler(filament::Texture::Sampler::SAMPLER_2D); - if (!is_srgb) { - builder.usage(filament::Texture::Usage::GEN_MIPMAPPABLE | - filament::Texture::Usage::SAMPLEABLE | - filament::Texture::Usage::UPLOADABLE); - } - filament::Texture* texture = builder.build(*engine); - - if (data) { - const size_t num_bytes = width * height * sizeof(uint8_t) * num_channels; - const filament::Texture::Format format = GetTextureFormat(num_channels); - texture->setImage( - *engine, 0, - filament::Texture::PixelBufferDescriptor( - data, num_bytes, format, filament::Texture::Type::UBYTE)); - if (!is_srgb) { - texture->generateMipmaps(*engine); - } - } - return texture; -} - -filament::Texture* CreateCubeTexture(filament::Engine* engine, int width, - int height, int num_channels, - const uint8_t* data, bool is_srgb) { - if (num_channels != 3) { - mju_error("Only support RGB cubemaps."); - return nullptr; - } - - const int kNumFacesPerCube = 6; - - int face_height = height; - if (width != height) { - if (width * kNumFacesPerCube != height) { - mju_error("Cube maps must contain 6 square images."); - } - face_height = height / kNumFacesPerCube; - } - if (width != face_height) { - mju_error("Cube map faces must be square."); - } - - filament::Texture::Builder builder; - builder.width(width); - builder.height(face_height); - builder.format(GetTextureInternalFormat(num_channels, is_srgb)); - builder.sampler(filament::Texture::Sampler::SAMPLER_CUBEMAP); - if (!is_srgb) { - builder.usage(filament::Texture::Usage::GEN_MIPMAPPABLE | - filament::Texture::Usage::SAMPLEABLE | - filament::Texture::Usage::UPLOADABLE); - } - filament::Texture* texture = builder.build(*engine); - - const int face_size = width * face_height * num_channels; - const int num_bytes = face_size * kNumFacesPerCube; - - uint8_t* buffer = new uint8_t[num_bytes]; - auto callback = +[](void* buffer, size_t size, void* user) { - delete [] reinterpret_cast(buffer); - }; - - filament::Texture::FaceOffsets offsets(face_size); - if (width == height) { - // Copy the image to all the faces. - for (int i = 0; i < kNumFacesPerCube; ++i) { - std::memcpy(buffer + (i * face_size), data, face_size); - } +Texture::Texture(filament::Engine* engine, TextureType texture_type, + mjtColorSpace color_space, int width, int height, + int num_channels, const uint8_t* data) + : engine_(engine) { + const bool is_srgb = color_space == mjCOLORSPACE_SRGB; + if (texture_type == TextureType::kCube) { + CreateCubeTexture(width, height, num_channels, data, is_srgb); + } else if (texture_type == TextureType::kNormal2d) { + Create2dTexture(width, height, num_channels, data, is_srgb); + } else if (texture_type == TextureType::kKtx) { + CreateKtxTexture(data, width * height * num_channels); } else { - // Use the cubemap as is. - std::memcpy(buffer, data, num_bytes); + mju_error("Unsupported texture type: %d", static_cast(texture_type)); } - - if (data) { - filament::Texture::PixelBufferDescriptor desc( - buffer, num_bytes, filament::Texture::Format::RGB, - filament::Texture::Type::UBYTE, callback); - texture->setImage(*engine, 0, std::move(desc), offsets); - if (!is_srgb) { - texture->generateMipmaps(*engine); - } - } - return texture; } -filament::Texture* CreateRenderTargetTexture( - filament::Engine* engine, int width, int height, - RenderTargetTextureType type) { +Texture::Texture(filament::Engine* engine, RenderTargetTextureType type, + int width, int height) : engine_(engine) { filament::Texture::Builder builder; builder.width(width); builder.height(height); @@ -198,36 +114,115 @@ filament::Texture* CreateRenderTargetTexture( default: mju_error("Unknown type: %d", static_cast(type)); } - return builder.build(*engine); + texture_ = builder.build(*engine); } -filament::Texture* CreateKtxTexture( - filament::Engine* engine, const uint8_t* data, int size, - filament::math::float3* spherical_harmonics_out) { +void Texture::Create2dTexture(int width, int height, int num_channels, + const uint8_t* data, bool is_srgb) { + if (num_channels != 1 && num_channels != 3 && num_channels != 4) { + mju_error("Unsupported number of channels: %d", num_channels); + return; + } + + filament::Texture::Builder builder; + builder.width(width); + builder.height(height); + builder.format(GetTextureInternalFormat(num_channels, is_srgb)); + builder.sampler(filament::Texture::Sampler::SAMPLER_2D); + if (!is_srgb) { + builder.usage(filament::Texture::Usage::GEN_MIPMAPPABLE | + filament::Texture::Usage::SAMPLEABLE | + filament::Texture::Usage::UPLOADABLE); + } + texture_ = builder.build(*engine_); + + if (data) { + const size_t num_bytes = width * height * sizeof(uint8_t) * num_channels; + const filament::Texture::Format format = GetTextureFormat(num_channels); + texture_->setImage( + *engine_, 0, + filament::Texture::PixelBufferDescriptor( + data, num_bytes, format, filament::Texture::Type::UBYTE)); + if (!is_srgb) { + texture_->generateMipmaps(*engine_); + } + } +} + +void Texture::CreateCubeTexture(int width, int height, int num_channels, + const uint8_t* data, bool is_srgb) { + if (num_channels != 3) { + mju_error("Only support RGB cubemaps."); + return; + } + + const int kNumFacesPerCube = 6; + + int face_height = height; + if (width != height) { + if (width * kNumFacesPerCube != height) { + mju_error("Cube maps must contain 6 square images."); + } + face_height = height / kNumFacesPerCube; + } + if (width != face_height) { + mju_error("Cube map faces must be square."); + } + + filament::Texture::Builder builder; + builder.width(width); + builder.height(face_height); + builder.format(GetTextureInternalFormat(num_channels, is_srgb)); + builder.sampler(filament::Texture::Sampler::SAMPLER_CUBEMAP); + if (!is_srgb) { + builder.usage(filament::Texture::Usage::GEN_MIPMAPPABLE | + filament::Texture::Usage::SAMPLEABLE | + filament::Texture::Usage::UPLOADABLE); + } + texture_ = builder.build(*engine_); + + const int face_size = width * face_height * num_channels; + const int num_bytes = face_size * kNumFacesPerCube; + + uint8_t* buffer = new uint8_t[num_bytes]; + auto callback = +[](void* buffer, size_t size, void* user) { + delete [] reinterpret_cast(buffer); + }; + + filament::Texture::FaceOffsets offsets(face_size); + if (width == height) { + // Copy the image to all the faces. + for (int i = 0; i < kNumFacesPerCube; ++i) { + std::memcpy(buffer + (i * face_size), data, face_size); + } + } else { + // Use the cubemap as is. + std::memcpy(buffer, data, num_bytes); + } + + if (data) { + filament::Texture::PixelBufferDescriptor desc( + buffer, num_bytes, filament::Texture::Format::RGB, + filament::Texture::Type::UBYTE, callback); + texture_->setImage(*engine_, 0, std::move(desc), offsets); + if (!is_srgb) { + texture_->generateMipmaps(*engine_); + } + } +} + +void Texture::CreateKtxTexture(const uint8_t* data, int size) { image::Ktx1Bundle* bundle = new image::Ktx1Bundle(data, size); - if (spherical_harmonics_out) { - bundle->getSphericalHarmonics(spherical_harmonics_out); - } + has_spherical_harmonics_ = true; + bundle->getSphericalHarmonics(spherical_harmonics_); const bool is_srgb = false; - return ktxreader::Ktx1Reader::createTexture(engine, bundle, is_srgb); + texture_ = ktxreader::Ktx1Reader::createTexture(engine_, bundle, is_srgb); } -filament::Texture* CreateTexture(filament::Engine* engine, const mjModel* model, - int id, TextureType texture_type) { - if (id < 0 || id >= model->ntex) { - mju_error("Invalid texture index %d", id); +Texture::~Texture() { + if (texture_) { + engine_->destroy(texture_); } - - const int width = model->tex_width[id]; - const int height = model->tex_height[id]; - const bool is_srgb = model->tex_colorspace[id] == mjCOLORSPACE_SRGB; - const int num_channels = model->tex_nchannel[id]; - const mjtByte* data = model->tex_data + model->tex_adr[id]; - filament::Texture* texture = - texture_type == TextureType::kNormal2d - ? Create2dTexture(engine, width, height, num_channels, data, is_srgb) - : CreateCubeTexture(engine, width, height, num_channels, data, - is_srgb); - return texture; } + } // namespace mujoco diff --git a/src/experimental/filament/filament/texture_util.h b/src/experimental/filament/filament/texture_util.h index dd267af8..42925325 100644 --- a/src/experimental/filament/filament/texture_util.h +++ b/src/experimental/filament/filament/texture_util.h @@ -40,30 +40,43 @@ enum class RenderTargetTextureType { kReflectionColor, }; -// Creates a filament Texture for the given 2D texture. -filament::Texture* Create2dTexture(filament::Engine* engine, int width, - int height, int num_channels, - const uint8_t* data, bool is_srgb); +class Texture { + public: + // Creates a texture with the given data. + Texture(filament::Engine* engine, TextureType texture_type, + mjtColorSpace color_space, int width, int height, int num_channels, + const uint8_t* data); -// Creates a filament Texture for the given cube texture. -filament::Texture* CreateCubeTexture(filament::Engine* engine, int width, - int height, int num_channels, - const uint8_t* data, bool is_srgb); + // Creates a texture for use with a render target. + Texture(filament::Engine* engine, RenderTargetTextureType type, int width, + int height); -// Creates a filament Texture for the given KTX payload. -filament::Texture* CreateKtxTexture( - filament::Engine* engine, const uint8_t* data, int size, - filament::math::float3* spherical_harmonics_out); + ~Texture(); -// Creates a filament Texture for the given texture in the mjModel. -filament::Texture* CreateTexture(filament::Engine* engine, const mjModel* model, - int id, TextureType texture_type); + filament::Texture* GetFilamentTexture() const { return texture_; } -// Creates a filament Texture for the given render target. -filament::Texture* CreateRenderTargetTexture(filament::Engine* engine, - int width, int height, - RenderTargetTextureType type); + using SphericalHarmonics = filament::math::float3[9]; + const SphericalHarmonics* GetSphericalHarmonics() const { + return has_spherical_harmonics_ ? &spherical_harmonics_ : nullptr; + } + + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + + private: + + void Create2dTexture(int width, int height, int num_channels, + const uint8_t* data, bool is_srgb); + void CreateCubeTexture(int width, int height, int num_channels, + const uint8_t* data, bool is_srgb); + void CreateKtxTexture(const uint8_t* data, int size); + + filament::Engine* engine_ = nullptr; + filament::Texture* texture_ = nullptr; + SphericalHarmonics spherical_harmonics_; + bool has_spherical_harmonics_ = false; +}; } // namespace mujoco #endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_TEXTURE_UTIL_H_