Introduce a Texture class that wraps filament::Texture.

PiperOrigin-RevId: 892839636
Change-Id: I3c08fba0a514cfb758ec2842d8510c6d88300874
This commit is contained in:
Haroon Qureshi
2026-04-01 03:51:23 -07:00
committed by Copybara-Service
parent 6d32038417
commit 95666da039
12 changed files with 292 additions and 315 deletions
@@ -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).
@@ -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);
+21 -64
View File
@@ -21,6 +21,7 @@
#include <filament/TextureSampler.h>
#include <mujoco/mjmodel.h>
#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
+10 -10
View File
@@ -17,11 +17,11 @@
#include <filament/Engine.h>
#include <filament/MaterialInstance.h>
#include <filament/Texture.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#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) {
@@ -15,6 +15,7 @@
#include "experimental/filament/filament/model_objects.h"
#include <array>
#include <memory>
#include <utility>
#include <vector>
@@ -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<Texture>(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;
}
}
@@ -16,6 +16,7 @@
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_MODEL_OBJECTS_H_
#include <array>
#include <memory>
#include <unordered_map>
#include <vector>
@@ -25,6 +26,7 @@
#include <math/vec3.h>
#include <mujoco/mjmodel.h>
#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<filament::Skybox*> skyboxes_;
@@ -88,8 +88,7 @@ class ModelObjects {
std::unordered_map<int, FilamentBuffers> meshes_;
std::unordered_map<int, FilamentBuffers> convex_hulls_;
std::unordered_map<int, FilamentBuffers> height_fields_;
std::unordered_map<int, filament::Texture*> textures_;
std::unordered_map<int, SphericalHarmonics> spherical_harmonics_;
std::unordered_map<int, std::unique_ptr<Texture>> textures_;
float specular_multiplier_ = 0.2f;
float shininess_multiplier_ = 0.1f;
float emissive_multiplier_ = 0.3f;
@@ -15,6 +15,7 @@
#include "experimental/filament/filament/object_manager.h"
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
@@ -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<Texture>(
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<Texture>(
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<Texture>(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<Texture>(
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<const uint8_t*>(asset.payload),
asset.size, spherical_harmonics);
fallback_indirect_light_texture_ = std::make_unique<Texture>(
engine_, TextureType::kKtx, mjCOLORSPACE_AUTO, asset.size, 1, 1,
reinterpret_cast<const uint8_t*>(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(
@@ -16,12 +16,14 @@
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_OBJECT_MANAGER_H_
#include <array>
#include <memory>
#include <string_view>
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/Skybox.h>
#include <mujoco/mujoco.h>
#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<filament::Material*, kNumMaterials> materials_;
std::array<filament::Texture*, mjNTEXROLE> 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<Texture*, mjNTEXROLE> fallback_textures_;
std::unique_ptr<Texture> fallback_white_ = nullptr;
std::unique_ptr<Texture> fallback_black_ = nullptr;
std::unique_ptr<Texture> fallback_normal_ = nullptr;
std::unique_ptr<Texture> fallback_orm_ = nullptr;
std::unique_ptr<Texture> fallback_indirect_light_texture_ = nullptr;
filament::IndirectLight* fallback_indirect_light_ = nullptr;
};
@@ -14,15 +14,15 @@
#include "experimental/filament/filament/render_target_util.h"
#include <memory>
#include <filament/Engine.h>
#include <filament/RenderTarget.h>
#include <filament/Texture.h>
#include <mujoco/mujoco.h>
#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<Texture>(engine_, color_type_, width, height);
depth_texture_ =
CreateRenderTargetTexture(engine_, width, height, depth_type_);
std::make_unique<Texture>(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
@@ -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 <memory>
#include <filament/Engine.h>
#include <filament/Texture.h>
#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<Texture> color_texture_ = nullptr;
std::unique_ptr<Texture> depth_texture_ = nullptr;
RenderTargetTextureType color_type_;
RenderTargetTextureType depth_type_;
int width_ = 0;
+117 -122
View File
@@ -23,7 +23,6 @@
#include <filament/Texture.h>
#include <image/Ktx1Bundle.h>
#include <ktxreader/Ktx1Reader.h>
#include <math/vec3.h>
#include <mujoco/mujoco.h>
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<uint8_t*>(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<int>(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<int>(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<uint8_t*>(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
@@ -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_