Files
Mujoco_WASM/src/render/filament/core/light.cc
T
Haroon Qureshi 96781fa793 Move filament renderer out of experimental into src/render/filament.
Also:
- renames the main header file to mjrfilament.h.
- moves filament_util under a "support" folder.
PiperOrigin-RevId: 930448415
Change-Id: Iad89756aaf1ddf4c3e67e1afc3a13776f71fa20f
2026-06-11 04:43:20 -07:00

199 lines
5.8 KiB
C++

// Copyright 2025 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "render/filament/core/light.h"
#include <numbers>
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/LightManager.h>
#include <filament/Scene.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <utils/Entity.h>
#include <utils/EntityManager.h>
#include <mujoco/mujoco.h>
#include "render/filament/core/texture.h"
#include "render/filament/mjrfilament.h"
#include "render/filament/support/filament_util.h"
namespace mujoco {
using filament::math::float3;
using filament::math::mat3f;
Light::Light(filament::Engine* engine, const mjrfLightParams& params)
: engine_(engine), params_(params) {
// Filament treats image-based lights (IBLs) as separate objects (i.e.
// filament::IndirectLight) and so we need to handle IBLs specially.
if (params.type == mjLIGHT_IMAGE) {
filament::IndirectLight::Builder builder;
if (params.texture) {
// Allow null textures for fallback lights.
const Texture* texture = Texture::downcast(params.texture);
builder.reflections(texture->GetFilamentTexture());
const Texture::SphericalHarmonics* spherical_harmonics =
texture->GetSphericalHarmonics();
if (spherical_harmonics != nullptr) {
builder.irradiance(3, *spherical_harmonics);
}
}
builder.intensity(params.intensity);
// Rotate the light to match mujoco's Z-up convention.
builder.rotation(mat3f::rotation(std::numbers::pi / 2, float3{1, 0, 0}));
ibl_ = builder.build(*engine_);
return;
}
filament::LightManager::Type type;
switch (params.type) {
case mjLIGHT_SPOT:
type = filament::LightManager::Type::FOCUSED_SPOT;
break;
case mjLIGHT_DIRECTIONAL:
type = filament::LightManager::Type::DIRECTIONAL;
break;
case mjLIGHT_POINT:
type = filament::LightManager::Type::POINT;
break;
default:
mju_error("Unsupported light type: %d", params.type);
return;
}
filament::LightManager::Builder builder(type);
builder.color(ReadFloat3(params.color));
builder.intensityCandela(params.intensity);
builder.castShadows(params.cast_shadows);
if (type == filament::LightManager::Type::FOCUSED_SPOT) {
builder.spotLightCone(0,
params.spot_cone_angle * std::numbers::pi / 180.0f);
}
if (type != filament::LightManager::Type::DIRECTIONAL) {
builder.falloff(params.range);
}
filament::LightManager::ShadowOptions opts;
opts.mapSize = 4096;
opts.shadowCascades =
type == filament::LightManager::Type::DIRECTIONAL ? 4 : 1;
opts.shadowBulbRadius = params.bulb_radius;
opts.mapSize = params.shadow_map_size;
if (params.vsm_blur_width > 0.0f) {
opts.vsm.elvsm = true;
opts.vsm.blurWidth = params.vsm_blur_width;
}
builder.shadowOptions(opts);
entity_ = utils::EntityManager::get().create();
if (entity_.isNull()) {
mju_error("Failed to create light entity.");
}
builder.build(*engine_, entity_);
}
Light::~Light() noexcept {
if (ibl_) {
engine_->destroy(ibl_);
} else {
utils::EntityManager& em = utils::EntityManager::get();
if (!entity_.isNull()) {
engine_->destroy(entity_);
em.destroy(entity_);
}
}
}
void Light::AddToScene(filament::Scene* scene) {
if (ibl_) {
scene->setIndirectLight(ibl_);
} else {
scene->addEntity(entity_);
}
}
void Light::RemoveFromScene(filament::Scene* scene) {
if (ibl_) {
scene->setIndirectLight(nullptr);
} else {
scene->remove(entity_);
}
}
void Light::SetTransform(filament::math::float3 position,
filament::math::float3 direction) {
if (!ibl_) {
filament::LightManager& lm = engine_->getLightManager();
const filament::LightManager::Instance li = lm.getInstance(entity_);
lm.setPosition(li, position);
lm.setDirection(li, direction);
}
}
void Light::SetColor(const filament::math::float3& color) {
if (!ibl_) {
params_.color[0] = color.r;
params_.color[1] = color.g;
params_.color[2] = color.b;
filament::LightManager& lm = engine_->getLightManager();
const filament::LightManager::Instance li = lm.getInstance(entity_);
lm.setColor(li, color);
}
}
void Light::SetIntensity(float intensity) {
params_.intensity = intensity;
if (ibl_) {
ibl_->setIntensity(intensity);
} else {
filament::LightManager& lm = engine_->getLightManager();
const filament::LightManager::Instance li = lm.getInstance(entity_);
lm.setIntensityCandela(li, intensity);
}
}
void Light::Enable() {
if (!enabled_) {
enabled_ = true;
if (ibl_) {
ibl_->setIntensity(params_.intensity);
} else {
filament::LightManager& lm = engine_->getLightManager();
const filament::LightManager::Instance li = lm.getInstance(entity_);
lm.setLightChannel(li, 0, enabled_);
}
}
}
void Light::Disable() {
if (enabled_) {
enabled_ = false;
if (ibl_) {
ibl_->setIntensity(0.f);
} else {
filament::LightManager& lm = engine_->getLightManager();
const filament::LightManager::Instance li = lm.getInstance(entity_);
lm.setLightChannel(li, 0, enabled_);
}
}
}
mjtLightType Light::GetType() const {
return static_cast<mjtLightType>(params_.type);
}
} // namespace mujoco