/* SPDX-FileCopyrightText: 2011-2022 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "instancer.hh" #include #include #include "BKE_duplilist.hh" #include "BKE_particle.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "DEG_depsgraph_query.hh" #include "DNA_particle_types.h" #include "hydra_scene_delegate.hh" namespace blender::io::hydra { InstancerData::InstancerData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id) : IdData(scene_delegate, nullptr, prim_id) { } void InstancerData::init() {} void InstancerData::insert() {} void InstancerData::remove() { CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", prim_id.GetText()); for (auto &m_inst : mesh_instances_.values()) { m_inst.data->remove(); } if (!mesh_instances_.is_empty()) { scene_delegate_->GetRenderIndex().RemoveInstancer(prim_id); } mesh_instances_.clear(); for (auto &l_inst : nonmesh_instances_.values()) { l_inst.transforms.clear(); update_nonmesh_instance(l_inst); } nonmesh_instances_.clear(); } void InstancerData::update() {} pxr::VtValue InstancerData::get_data(pxr::TfToken const &key) const { ID_LOG(3, "%s", key.GetText()); if (key == pxr::HdInstancerTokens->instanceTransforms) { return pxr::VtValue(mesh_transforms_); } return pxr::VtValue(); } pxr::GfMatrix4d InstancerData::transform(pxr::SdfPath const &id) const { NonmeshInstance *nm_inst = nonmesh_instance(id); if (nm_inst) { return nm_inst->transforms[nonmesh_prim_id_index(id)]; } /* Mesh instance transform must be identity */ return pxr::GfMatrix4d(1.0); } pxr::HdPrimvarDescriptorVector InstancerData::primvar_descriptors( pxr::HdInterpolation interpolation) const { pxr::HdPrimvarDescriptorVector primvars; if (interpolation == pxr::HdInterpolationInstance) { primvars.emplace_back( pxr::HdInstancerTokens->instanceTransforms, interpolation, pxr::HdPrimvarRoleTokens->none); } return primvars; } pxr::VtIntArray InstancerData::indices(pxr::SdfPath const &id) const { return mesh_instance(id)->indices; } ObjectData *InstancerData::object_data(pxr::SdfPath const &id) const { MeshInstance *m_inst = mesh_instance(id); if (m_inst) { return m_inst->data.get(); } NonmeshInstance *nm_inst = nonmesh_instance(id); if (nm_inst) { return nm_inst->data.get(); } return nullptr; } pxr::SdfPathVector InstancerData::prototypes() const { pxr::SdfPathVector paths; for (const auto &m_inst : mesh_instances_.values()) { for (auto &p : m_inst.data->submesh_paths()) { paths.push_back(p); } } return paths; } void InstancerData::available_materials(Set &paths) const { for (const auto &m_inst : mesh_instances_.values()) { m_inst.data->available_materials(paths); } for (const auto &l_inst : nonmesh_instances_.values()) { l_inst.data->available_materials(paths); } } void InstancerData::update_double_sided(MaterialData *mat_data) { for (auto &m_inst : mesh_instances_.values()) { m_inst.data->update_double_sided(mat_data); } } void InstancerData::pre_update() { mesh_transforms_.clear(); for (auto &m_inst : mesh_instances_.values()) { m_inst.indices.clear(); } for (auto &l_inst : nonmesh_instances_.values()) { l_inst.transforms.clear(); } } void InstancerData::update_instance(DupliObject *dupli) { Object *object = dupli->ob; pxr::SdfPath p_id = object_prim_id(object); if (ObjectData::is_mesh(object)) { MeshInstance *m_inst = mesh_instance(p_id); if (!m_inst) { m_inst = &mesh_instances_.lookup_or_add_default(p_id); m_inst->data = std::make_unique(scene_delegate_, object, p_id); m_inst->data->init(); m_inst->data->insert(); } else { m_inst->data->update(); } ID_LOG(2, "Mesh %s %d", m_inst->data->id->name, int(mesh_transforms_.size())); m_inst->indices.push_back(mesh_transforms_.size()); mesh_transforms_.push_back(gf_matrix_from_transform(dupli->mat)); } else { NonmeshInstance *nm_inst = nonmesh_instance(p_id); if (!nm_inst) { nm_inst = &nonmesh_instances_.lookup_or_add_default(p_id); nm_inst->data = ObjectData::create(scene_delegate_, object, p_id); } ID_LOG(2, "Nonmesh %s %d", nm_inst->data->id->name, int(nm_inst->transforms.size())); nm_inst->transforms.push_back(gf_matrix_from_transform(dupli->mat)); } LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { if (psys_in_edit_mode(scene_delegate_->depsgraph, psys)) { continue; } if (HairData::is_supported(psys) && HairData::is_visible(scene_delegate_, object, psys)) { pxr::SdfPath h_id = hair_prim_id(object, psys); NonmeshInstance *nm_inst = nonmesh_instance(h_id); if (!nm_inst) { nm_inst = &nonmesh_instances_.lookup_or_add_default(h_id); nm_inst->data = std::make_unique(scene_delegate_, object, h_id, psys); nm_inst->data->init(); } ID_LOG(2, "Nonmesh %s %d", nm_inst->data->id->name, int(nm_inst->transforms.size())); nm_inst->transforms.push_back(gf_matrix_from_transform(psys->imat) * gf_matrix_from_transform(dupli->mat)); } } } void InstancerData::post_update() { /* Remove mesh instances without indices. */ mesh_instances_.remove_if([&](auto item) { bool res = item.value.indices.empty(); if (res) { item.value.data->remove(); } return res; }); /* Update light instances and remove instances without transforms. */ for (auto &l_inst : nonmesh_instances_.values()) { update_nonmesh_instance(l_inst); } nonmesh_instances_.remove_if([&](auto item) { return item.value.transforms.empty(); }); /* Insert/remove/update instancer in RenderIndex. */ pxr::HdRenderIndex &index = scene_delegate_->GetRenderIndex(); if (mesh_instances_.is_empty()) { /* Important: removing instancer when nonmesh_instances_ are empty too */ if (index.HasInstancer(prim_id) && nonmesh_instances_.is_empty()) { index.RemoveInstancer(prim_id); ID_LOG(1, "Remove instancer"); } } else { if (index.HasInstancer(prim_id)) { index.GetChangeTracker().MarkInstancerDirty(prim_id, pxr::HdChangeTracker::AllDirty); ID_LOG(1, "Update instancer"); } else { index.InsertInstancer(scene_delegate_, prim_id); ID_LOG(1, "Insert instancer"); } } } pxr::SdfPath InstancerData::object_prim_id(Object *object) const { /* Making id of object in form like _ */ char name[32]; SNPRINTF(name, "O_%p", object); return prim_id.AppendElementString(name); } pxr::SdfPath InstancerData::hair_prim_id(Object *parent_obj, const ParticleSystem *psys) const { /* Making id of object in form like _ */ char name[128]; SNPRINTF(name, "%s_PS_%p", object_prim_id(parent_obj).GetName().c_str(), psys); return prim_id.AppendElementString(name); } pxr::SdfPath InstancerData::nonmesh_prim_id(pxr::SdfPath const &prim_id, int index) const { char name[16]; SNPRINTF(name, "NM_%08d", index); return prim_id.AppendElementString(name); } int InstancerData::nonmesh_prim_id_index(pxr::SdfPath const &id) const { int index; sscanf(id.GetName().c_str(), "NM_%d", &index); return index; } void InstancerData::update_nonmesh_instance(NonmeshInstance &nm_inst) { ObjectData *obj_data = nm_inst.data.get(); pxr::SdfPath prev_id = nm_inst.data->prim_id; int i; /* Remove old Nonmesh instances */ while (nm_inst.count > nm_inst.transforms.size()) { --nm_inst.count; obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count); obj_data->remove(); } /* NOTE: Special case: recreate instances when prim_type was changed. * Doing only update for other Nonmesh objects. */ LightData *l_data = dynamic_cast(obj_data); if (l_data && l_data->prim_type((Light *)((Object *)l_data->id)->data) != l_data->prim_type_) { for (i = 0; i < nm_inst.count; ++i) { obj_data->prim_id = nonmesh_prim_id(prev_id, i); obj_data->remove(); } l_data->init(); for (i = 0; i < nm_inst.count; ++i) { obj_data->prim_id = nonmesh_prim_id(prev_id, i); obj_data->insert(); } } else { for (i = 0; i < nm_inst.count; ++i) { obj_data->prim_id = nonmesh_prim_id(prev_id, i); obj_data->update(); } } /* Add new Nonmesh instances */ while (nm_inst.count < nm_inst.transforms.size()) { obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count); obj_data->insert(); ++nm_inst.count; } obj_data->prim_id = prev_id; } InstancerData::MeshInstance *InstancerData::mesh_instance(pxr::SdfPath const &id) const { const auto *m_inst = mesh_instances_.lookup_ptr( id.GetPathElementCount() == 4 ? id.GetParentPath() : id); if (!m_inst) { return nullptr; } return const_cast(m_inst); } InstancerData::NonmeshInstance *InstancerData::nonmesh_instance(pxr::SdfPath const &id) const { const auto *nm_inst = nonmesh_instances_.lookup_ptr( id.GetPathElementCount() == 4 ? id.GetParentPath() : id); if (!nm_inst) { return nullptr; } return const_cast(nm_inst); } } // namespace blender::io::hydra