Skip to content

Commit 53b3137

Browse files
haroonqcopybara-github
authored andcommitted
Separate Light management into a separate class.
PiperOrigin-RevId: 918508291 Change-Id: I9cadc1b612006a0450f8a2fd9088d56cc2e610a3
1 parent fa8ac7a commit 53b3137

5 files changed

Lines changed: 291 additions & 185 deletions

File tree

src/experimental/filament/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ target_sources(${MUJOCO_FILAMENT_TARGET_NAME}
5151
filament/scene_view.h
5252
filament/texture.cc
5353
filament/texture.h
54+
compat/light_manager.cc
55+
compat/light_manager.h
5456
compat/model_objects.cc
5557
compat/model_objects.h
5658
compat/scene_bridge.cc
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// Copyright 2025 DeepMind Technologies Limited
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "experimental/filament/compat/light_manager.h"
16+
17+
#include <memory>
18+
#include <string>
19+
#include <utility>
20+
21+
#include <math/mat4.h>
22+
#include <math/mathfwd.h>
23+
#include <math/vec3.h>
24+
#include <math/vec4.h>
25+
#include <mujoco/mujoco.h>
26+
#include "experimental/filament/compat/model_objects.h"
27+
#include "experimental/filament/filament_util.h"
28+
#include "experimental/filament/render_context_filament_cpp.h"
29+
#include "experimental/filament/render_context_filament.h"
30+
31+
namespace mujoco {
32+
33+
using filament::math::float3;
34+
using filament::math::float4;
35+
using filament::math::mat3;
36+
using filament::math::mat4;
37+
38+
static UniquePtr<mjrTexture> CreateFallbackIndirectLightTexture(
39+
mjrfContext* ctx) {
40+
const std::string filename = ResolveFilamentAssetPath("ibl.ktx");
41+
mjResource* resource =
42+
mju_openResource("", filename.c_str(), nullptr, nullptr, 0);
43+
if (!resource) {
44+
mju_error("Failed to open resource: %s", filename.c_str());
45+
}
46+
const void* bytes = nullptr;
47+
const int nbytes = mju_readResource(resource, &bytes);
48+
if (bytes == nullptr || nbytes <= 0) {
49+
mju_error("Failed to read resource: %s", filename.c_str());
50+
}
51+
52+
mjrTextureConfig config;
53+
mjr_defaultTextureConfig(&config);
54+
config.width = 1;
55+
config.height = 1;
56+
config.sampler_type = mjTEXTURE_CUBE;
57+
config.format = mjPIXEL_FORMAT_KTX;
58+
config.color_space = mjCOLORSPACE_AUTO;
59+
60+
auto texture = CreateTexture(ctx, config);
61+
62+
mjrTextureData payload;
63+
mjr_defaultTextureData(&payload);
64+
payload.bytes = bytes;
65+
payload.nbytes = nbytes;
66+
payload.release_callback = +[](void* user_data) {
67+
mju_closeResource((mjResource*)user_data);
68+
};
69+
payload.user_data = resource;
70+
71+
mjrf_setTextureData(texture.get(), &payload);
72+
return texture;
73+
}
74+
75+
LightManager::LightManager(mjrfContext* ctx, mjrScene* scene,
76+
ModelObjects* model_objects)
77+
: ctx_(ctx), scene_(scene) {
78+
const mjModel* model = model_objects->GetModel();
79+
default_shadow_map_size_ = ReadElement(
80+
model, "filament.shadows.map_size", default_shadow_map_size_);
81+
default_vsm_blur_width_ = ReadElement(
82+
model, "filament.shadows.vsm_blur_width", default_vsm_blur_width_);
83+
fallback_head_light_intensity_ =
84+
ReadElement(model, "filament.fallback.head_light_intensity",
85+
fallback_head_light_intensity_);
86+
fallback_scene_light_intensity_ =
87+
ReadElement(model, "filament.fallback.scene_light_intensity",
88+
fallback_scene_light_intensity_);
89+
fallback_environment_light_intensity_ =
90+
ReadElement(model, "filament.fallback.environment_light_intensity",
91+
fallback_environment_light_intensity_);
92+
Prepare(model_objects);
93+
}
94+
95+
LightManager::~LightManager() {
96+
for (auto& iter : lights_) {
97+
mjrf_removeLightFromScene(scene_, iter.get());
98+
}
99+
lights_.clear();
100+
if (fallback_ibl_) {
101+
mjrf_removeLightFromScene(scene_, fallback_ibl_.get());
102+
}
103+
fallback_ibl_.reset();
104+
}
105+
106+
void LightManager::Prepare(ModelObjects* model_objects) {
107+
const mjModel* model = model_objects->GetModel();
108+
109+
bool has_image_based_light = false;
110+
float total_light_intensity = 0.0f;
111+
for (int i = 0; i < model->nlight; ++i) {
112+
total_light_intensity += model->light_intensity[i];
113+
114+
if (model->light_type[i] == mjLIGHT_IMAGE) {
115+
mjrLightParams params;
116+
mjr_defaultLightParams(&params);
117+
params.type = mjLIGHT_IMAGE;
118+
params.texture = model_objects->GetTexture(model->light_texid[i]);
119+
params.intensity = model->light_intensity[i];
120+
auto light_obj = CreateLight(ctx_, params);
121+
mjrf_addLightToScene(scene_, light_obj.get());
122+
lights_.emplace_back(std::move(light_obj));
123+
has_image_based_light = true;
124+
} else {
125+
mjrLightParams params;
126+
mjr_defaultLightParams(&params);
127+
params.color[0] = model->light_diffuse[0];
128+
params.color[1] = model->light_diffuse[1];
129+
params.color[2] = model->light_diffuse[2];
130+
params.type = (mjtLightType)model->light_type[i];
131+
params.cast_shadows = model->light_castshadow[i];
132+
params.bulb_radius = model->light_bulbradius[i];
133+
params.range = model->light_range[i];
134+
params.intensity = model->light_intensity[i];
135+
params.shadow_map_size = default_shadow_map_size_;
136+
params.vsm_blur_width = default_vsm_blur_width_;
137+
if (params.type == mjLIGHT_SPOT) {
138+
params.spot_cone_angle = model->light_cutoff[i];
139+
}
140+
141+
auto light_obj = CreateLight(ctx_, params);
142+
mjrf_addLightToScene(scene_, light_obj.get());
143+
lights_.emplace_back(std::move(light_obj));
144+
}
145+
}
146+
147+
// Add a placeholder (black) headlight as our last light. Going forward, we'll
148+
// assume lights_.back() is always the headlight.
149+
{
150+
mjrLightParams params;
151+
mjr_defaultLightParams(&params);
152+
// We break with the spec here slightly and use a spot light for the head
153+
// light instead of a directional params. This is because filament only
154+
// supports a single directional light, and we'd rather allow a scene
155+
// light to be that directional params. It's also a bit odd for a
156+
// directional light to move with the camera.
157+
params.type = mjLIGHT_SPOT;
158+
params.cast_shadows = 0;
159+
params.intensity = 0.0f;
160+
params.spot_cone_angle = 90.0f;
161+
auto light_obj = CreateLight(ctx_, params);
162+
mjrf_addLightToScene(scene_, light_obj.get());
163+
lights_.emplace_back(std::move(light_obj));
164+
}
165+
166+
if (!has_image_based_light && total_light_intensity > 0.0f) {
167+
// Create a black indirect light to ensure that the skybox is
168+
// oriented to respect mujoco's Z-up convention.
169+
mjrLightParams params;
170+
mjr_defaultLightParams(&params);
171+
params.type = mjLIGHT_IMAGE;
172+
params.intensity = 10.0f;
173+
fallback_ibl_ = CreateLight(ctx_, params);
174+
mjrf_addLightToScene(scene_, fallback_ibl_.get());
175+
}
176+
177+
// There are no "physical" lights in the scene which means we're likely
178+
// dealing with a "classic renderer" scene. In this case, let's add a
179+
// default environment light and set the light intensity ourselves.
180+
if (total_light_intensity == 0.0f) {
181+
// Create a fallback environment light.
182+
fallback_ibl_texture_ = CreateFallbackIndirectLightTexture(ctx_);
183+
184+
mjrLightParams params;
185+
mjr_defaultLightParams(&params);
186+
params.type = mjLIGHT_IMAGE;
187+
params.texture = fallback_ibl_texture_.get();
188+
params.intensity = fallback_environment_light_intensity_;
189+
fallback_ibl_ = CreateLight(ctx_, params);
190+
mjrf_addLightToScene(scene_, fallback_ibl_.get());
191+
192+
// Distribute the fallback scene light intensity among the lights.
193+
const float intensity = fallback_scene_light_intensity_ / lights_.size();
194+
for (auto& light : lights_) {
195+
if (light) {
196+
const bool is_headlight = (light == lights_.back());
197+
mjrf_setLightIntensity(light.get(),
198+
is_headlight ? fallback_head_light_intensity_
199+
: intensity);
200+
}
201+
}
202+
}
203+
204+
mjrf_setSceneSkybox(scene_, model_objects->GetSkyboxTexture());
205+
}
206+
207+
mjrLight* LightManager::GetLight(int index) {
208+
if (index < 0 || index >= lights_.size()) {
209+
return nullptr;
210+
}
211+
return lights_[index].get();
212+
}
213+
} // namespace mujoco
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 DeepMind Technologies Limited
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_LIGHT_MANAGER_H_
16+
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_LIGHT_MANAGER_H_
17+
18+
#include <vector>
19+
20+
#include "experimental/filament/compat/model_objects.h"
21+
#include "experimental/filament/render_context_filament.h"
22+
#include "experimental/filament/render_context_filament_cpp.h"
23+
24+
namespace mujoco {
25+
26+
// Manages Light entities for an mjrScene using data from an mjvScene.
27+
class LightManager {
28+
public:
29+
LightManager(mjrfContext* ctx, mjrScene* scene, ModelObjects* model_objects);
30+
~LightManager();
31+
32+
// Returns the light with the given index in the mjModel. Note that an extra
33+
// headlight is assigned of the index `nlight`.
34+
mjrLight* GetLight(int index);
35+
36+
LightManager(const LightManager&) = delete;
37+
LightManager& operator=(const LightManager&) = delete;
38+
39+
private:
40+
void Prepare(ModelObjects* model_objects);
41+
42+
mjrfContext* ctx_ = nullptr;
43+
mjrScene* scene_ = nullptr;
44+
UniquePtr<mjrLight> fallback_ibl_{nullptr, nullptr};
45+
UniquePtr<mjrTexture> fallback_ibl_texture_{nullptr, nullptr};
46+
std::vector<UniquePtr<mjrLight>> lights_;
47+
int default_shadow_map_size_ = 2048;
48+
float default_vsm_blur_width_ = 0.0f;
49+
float fallback_head_light_intensity_ = 0.f;
50+
float fallback_scene_light_intensity_ = 80'000.f;
51+
float fallback_environment_light_intensity_ = 5'000.f;
52+
};
53+
54+
} // namespace mujoco
55+
56+
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_LIGHT_MANAGER_H_

0 commit comments

Comments
 (0)