Hydra: add support for legacy hair particles #114197

Merged
Brecht Van Lommel merged 27 commits from DagerD/blender:hydra-add-legacy-hair-particles into main 2024-01-11 15:44:35 +01:00
8 changed files with 264 additions and 57 deletions

View File

@ -8,14 +8,25 @@
#include <pxr/imaging/hd/tokens.h>
#include "BKE_attribute.hh"
#include "BKE_material.h"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
#include "BKE_material.h"
#include "BKE_particle.h"
#include "DEG_depsgraph_query.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "hydra_scene_delegate.h"
namespace blender::io::hydra {
namespace usdtokens {
static const pxr::TfToken st("st", pxr::TfToken::Immortal);
}
CurvesData::CurvesData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id)
@ -27,8 +38,7 @@ void CurvesData::init()
{
ID_LOGN(1, "");
const Object *object = (const Object *)id;
write_curves((const Curves *)object->data);
write_curves();
write_transform();
write_materials();
}
@ -76,12 +86,12 @@ pxr::VtValue CurvesData::get_data(pxr::TfToken const &key) const
if (key == pxr::HdTokens->points) {
return pxr::VtValue(vertices_);
}
else if (key == pxr::HdPrimvarRoleTokens->textureCoordinate) {
return pxr::VtValue(uvs_);
}
else if (key == pxr::HdTokens->widths) {
return pxr::VtValue(widths_);
}
else if (key == usdtokens::st) {
return pxr::VtValue(uvs_);
}
return pxr::VtValue();
}
@ -121,11 +131,10 @@ pxr::HdPrimvarDescriptorVector CurvesData::primvar_descriptors(
primvars.emplace_back(pxr::HdTokens->widths, interpolation, pxr::HdPrimvarRoleTokens->none);
}
}
else if (interpolation == pxr::HdInterpolationConstant) {
else if (interpolation == pxr::HdInterpolationUniform) {
if (!uvs_.empty()) {
primvars.emplace_back(pxr::HdPrimvarRoleTokens->textureCoordinate,
interpolation,
pxr::HdPrimvarRoleTokens->textureCoordinate);
primvars.emplace_back(
usdtokens::st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
}
}
return primvars;
@ -142,8 +151,10 @@ void CurvesData::write_materials()
mat_data_ = get_or_create_material(mat);
}
void CurvesData::write_curves(const Curves *curves_id)
void CurvesData::write_curves()
{
Object *object = (Object *)id;
Curves *curves_id = (Curves *)object->data;
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
curve_vertex_counts_.resize(curves.curves_num());
@ -163,12 +174,6 @@ void CurvesData::write_curves(const Curves *curves_id)
widths_[i] = radii[i] * 2.0f;
}
write_uv_maps(curves_id);
}
void CurvesData::write_uv_maps(const Curves *curves_id)
{
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const Span<float2> surface_uv_coords = curves.surface_uv_coords();
if (surface_uv_coords.is_empty()) {
uvs_.clear();
@ -179,4 +184,104 @@ void CurvesData::write_uv_maps(const Curves *curves_id)
MutableSpan(uvs_.data(), uvs_.size()).copy_from(surface_uv_coords.cast<pxr::GfVec2f>());
}
HairData::HairData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system)
: CurvesData(scene_delegate, object, prim_id), particle_system_(particle_system)
{
}
bool HairData::is_supported(const ParticleSystem *particle_system)
{
return particle_system->part && particle_system->part->type == PART_HAIR;
DagerD marked this conversation as resolved Outdated

simplify to return particle_system->part && particle_system->part->type == PART_HAIR

simplify to `return particle_system->part && particle_system->part->type == PART_HAIR`
}
bool HairData::is_visible(HydraSceneDelegate *scene_delegate,
Object *object,
ParticleSystem *particle_system)
{
const bool for_render = (DEG_get_mode(scene_delegate->depsgraph) == DAG_EVAL_RENDER);
return psys_check_enabled(object, particle_system, for_render);
}
void HairData::update()
{
init();
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(
prim_id, pxr::HdChangeTracker::AllDirty);
ID_LOGN(1, "");
}
DagerD marked this conversation as resolved Outdated

This doesn't suit to scene delegate design, there shouldn't be uninited objects. This check has to be made before calling init()

This doesn't suit to scene delegate design, there shouldn't be uninited objects. This check has to be made before calling init()
void HairData::write_transform()
{
transform = pxr::GfMatrix4d(1.0);
}
void HairData::write_curves()
{
ParticleCacheKey **cache = particle_system_->pathcache;
if (cache == nullptr) {
return;
}
curve_vertex_counts_.clear();
curve_vertex_counts_.reserve(particle_system_->totpart);
vertices_.clear();
DagerD marked this conversation as resolved Outdated

This shouldn't be there. It should be on scene delegate side

This shouldn't be there. It should be on scene delegate side
widths_.clear();
uvs_.clear();
uvs_.reserve(particle_system_->totpart);
Object *object = (Object *)id;
float scale = particle_system_->part->rad_scale *
(std::abs(object->object_to_world[0][0]) +
std::abs(object->object_to_world[1][1]) +
std::abs(object->object_to_world[2][2])) /
3;
float root = scale * particle_system_->part->rad_root;
float tip = scale * particle_system_->part->rad_tip;
float shape = particle_system_->part->shape;
ParticleCacheKey *strand;
for (int pa_index = 0; pa_index < particle_system_->totpart; ++pa_index) {
strand = cache[pa_index];
int point_count = strand->segments + 1;
curve_vertex_counts_.push_back(point_count);
for (int point_index = 0; point_index < point_count; ++point_index, ++strand) {
vertices_.push_back(pxr::GfVec3f(strand->co));
float x = float(point_index) / (point_count - 1);
float radius = pow(x, pow(10.0f, -shape));
widths_.push_back(root + (tip - root) * radius);
}
if (particle_system_->part->shape_flag & PART_SHAPE_CLOSE_TIP) {
widths_[widths_.size() - 1] = 0.0f;
}
if (particle_system_->particles) {
ParticleData &pa = particle_system_->particles[pa_index];
ParticleSystemModifierData *psmd = psys_get_modifier(object, particle_system_);
int num = ELEM(pa.num_dmcache, DMCACHE_ISCHILD, DMCACHE_NOTFOUND) ? pa.num : pa.num_dmcache;
float r_uv[2] = {0.0f, 0.0f};
if (ELEM(psmd->psys->part->from, PART_FROM_FACE, PART_FROM_VOLUME) &&
!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD))
{
const MFace *mface = static_cast<const MFace *>(
CustomData_get_layer(&psmd->mesh_final->fdata_legacy, CD_MFACE));
const MTFace *mtface = static_cast<const MTFace *>(
CustomData_get_layer(&psmd->mesh_final->fdata_legacy, CD_MTFACE));
if (mface && mtface) {
mtface += num;
psys_interpolate_uvs(mtface, mface->v4, pa.fuv, r_uv);
}
}
uvs_.push_back(pxr::GfVec2f(r_uv[0], r_uv[1]));
}
}
}
} // namespace blender::io::hydra

View File

@ -7,19 +7,20 @@
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include "DNA_curves_types.h"
#include "BLI_set.hh"
#include "BKE_duplilist.h"
#include "DNA_curves_types.h"
#include "DNA_particle_types.h"
#include "material.h"
#include "object.h"
namespace blender::io::hydra {
class CurvesData : public ObjectData {
private:
protected:
pxr::VtIntArray curve_vertex_counts_;
pxr::VtVec3fArray vertices_;
pxr::VtVec2fArray uvs_;
@ -46,10 +47,29 @@ class CurvesData : public ObjectData {
protected:
void write_materials() override;
virtual void write_curves();
};
class HairData : public CurvesData {
private:
void write_curves(const Curves *curves_id);
void write_uv_maps(const Curves *curves_id);
ParticleSystem *particle_system_;
public:
HairData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system);
static bool is_supported(const ParticleSystem *particle_system);
static bool is_visible(HydraSceneDelegate *scene_delegate,
Object *object,
ParticleSystem *particle_system);
void update() override;
protected:
void write_transform() override;
void write_curves() override;
};
} // namespace blender::io::hydra

View File

@ -8,6 +8,8 @@
#include "DNA_scene_types.h"
#include "BKE_particle.h"
#include "BLI_set.hh"
#include "BLI_string.h"
@ -255,6 +257,16 @@ pxr::SdfPath HydraSceneDelegate::material_prim_id(const Material *mat) const
return prim_id((ID *)mat, "M");
}
pxr::SdfPath HydraSceneDelegate::hair_prim_id(Object *parent_obj, const ParticleSystem *psys) const
DagerD marked this conversation as resolved Outdated

consider name psys_prim_id, change pxr::SdfPath parent_obj to Object *obj

consider name `psys_prim_id`, change `pxr::SdfPath parent_obj` to `Object *obj`
{
char name[128];
SNPRINTF(name,
DagerD marked this conversation as resolved Outdated

Consider id as: <O_...>_<PS_...>

Consider id as: `<O_...>_<PS_...>`
"%s_%s",
object_prim_id(parent_obj).GetName().c_str(),
prim_id((ID *)psys, "PS").GetName().c_str());
return GetDelegateID().AppendElementString(name);
}
pxr::SdfPath HydraSceneDelegate::instancer_prim_id() const
{
return GetDelegateID().AppendElementString("Instancer");
@ -278,7 +290,6 @@ ObjectData *HydraSceneDelegate::object_data(pxr::SdfPath const &id) const
if (obj_data) {
return obj_data->get();
}
InstancerData *i_data = instancer_data(p_id, true);
if (i_data) {
return i_data->object_data(id);
@ -315,6 +326,11 @@ MaterialData *HydraSceneDelegate::material_data(pxr::SdfPath const &id) const
return mat_data->get();
}
HairData *HydraSceneDelegate::hair_data(pxr::SdfPath const &id) const
{
return dynamic_cast<HairData *>(object_data(id));
}
InstancerData *HydraSceneDelegate::instancer_data(pxr::SdfPath const &id, bool child_id) const
{
pxr::SdfPath p_id;
@ -421,6 +437,49 @@ void HydraSceneDelegate::update_collection()
instancer_data_->pre_update();
auto update_psys = [this, &available_objects](Object *object) {
LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
if (psys_in_edit_mode(depsgraph, psys)) {
continue;
}
if (HairData::is_supported(psys) && HairData::is_visible(this, object, psys)) {
pxr::SdfPath id = hair_prim_id(object, psys);
HairData *h_data = hair_data(id);
if (h_data) {
h_data->update();
}
else {
h_data = dynamic_cast<HairData *>(
objects_.lookup_or_add(id, std::make_unique<HairData>(this, object, id, psys))
.get());
h_data->init();
h_data->insert();
}
available_objects.add(id.GetName());
}
}
};
auto update_object = [this, &available_objects](Object *object) {
if (!ObjectData::is_supported(object) || !ObjectData::is_visible(this, object) ||
(!shading_settings.use_scene_lights && object->type == OB_LAMP))
{
return;
}
available_objects.add(object_prim_id(object).GetName());
pxr::SdfPath id = object_prim_id(object);
ObjectData *obj_data = object_data(id);
if (obj_data) {
obj_data->update();
}
else {
obj_data = objects_.lookup_or_add(id, ObjectData::create(this, object, id)).get();
obj_data->insert();
}
};
ITER_BEGIN (DEG_iterator_objects_begin,
DEG_iterator_objects_next,
DEG_iterator_objects_end,
@ -441,23 +500,8 @@ void HydraSceneDelegate::update_collection()
continue;
}
if (!ObjectData::is_supported(object) || !ObjectData::is_visible(this, object) ||
(!shading_settings.use_scene_lights && object->type == OB_LAMP))
{
continue;
}
available_objects.add(object_prim_id(object).GetName());
pxr::SdfPath id = object_prim_id(object);
ObjectData *obj_data = object_data(id);
if (obj_data) {
obj_data->update();
}
else {
obj_data = objects_.lookup_or_add(id, ObjectData::create(this, object, id)).get();
obj_data->insert();
}
update_psys(object);
update_object(object);
}
ITER_END;

View File

@ -94,6 +94,7 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
pxr::SdfPath prim_id(const ID *id, const char *prefix) const;
pxr::SdfPath object_prim_id(const Object *object) const;
pxr::SdfPath material_prim_id(const Material *mat) const;
pxr::SdfPath hair_prim_id(Object *parent_obj, const ParticleSystem *mat) const;
DagerD marked this conversation as resolved Outdated

Seems hair_prim_id suits better here

Seems hair_prim_id suits better here
pxr::SdfPath instancer_prim_id() const;
pxr::SdfPath world_prim_id() const;
@ -103,6 +104,7 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
VolumeData *volume_data(pxr::SdfPath const &id) const;
LightData *light_data(pxr::SdfPath const &id) const;
MaterialData *material_data(pxr::SdfPath const &id) const;
HairData *hair_data(pxr::SdfPath const &id) const;
InstancerData *instancer_data(pxr::SdfPath const &id, bool child_id = false) const;
void check_updates();

View File

@ -7,10 +7,15 @@
#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/light.h>
#include "BKE_particle.h"
#include "BLI_math_matrix.h"
#include "BLI_string.h"
#include "DEG_depsgraph_query.hh"
#include "DNA_particle_types.h"
#include "hydra_scene_delegate.h"
namespace blender::io::hydra {
@ -157,9 +162,27 @@ void InstancerData::update_instance(DupliObject *dupli)
nm_inst = &nonmesh_instances_.lookup_or_add_default(p_id);
nm_inst->data = ObjectData::create(scene_delegate_, object, p_id);
}
ID_LOG(2, "Light %s %d", nm_inst->data->id->name, int(nm_inst->transforms.size()));
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<HairData>(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()
@ -208,6 +231,14 @@ pxr::SdfPath InstancerData::object_prim_id(Object *object) const
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 <prefix>_<pointer in 16 hex digits format> */
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];
@ -228,17 +259,17 @@ void InstancerData::update_nonmesh_instance(NonmeshInstance &nm_inst)
pxr::SdfPath prev_id = nm_inst.data->prim_id;
int i;
/* Remove old light instances */
/* 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();
}
/* Update current light instances */
/* NOTE: Special case: recreate instances when prim_type was changed.
* Doing only update for other Nonmesh objects. */
LightData *l_data = dynamic_cast<LightData *>(obj_data);
if (l_data && l_data->prim_type((Light *)((Object *)l_data->id)->data) != l_data->prim_type_) {
/* Special case: recreate instances when prim_type was changed */
for (i = 0; i < nm_inst.count; ++i) {
obj_data->prim_id = nonmesh_prim_id(prev_id, i);
obj_data->remove();
@ -256,7 +287,7 @@ void InstancerData::update_nonmesh_instance(NonmeshInstance &nm_inst)
}
}
/* Add new light instances */
/* 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();

View File

@ -9,6 +9,8 @@
#include "mesh.h"
struct ParticleSystem;
namespace blender::io::hydra {
class InstancerData : public IdData {
@ -57,6 +59,7 @@ class InstancerData : public IdData {
private:
pxr::SdfPath object_prim_id(Object *object) const;
pxr::SdfPath hair_prim_id(Object *parent_obj, const ParticleSystem *psys) const;
pxr::SdfPath nonmesh_prim_id(pxr::SdfPath const &prim_id, int index) const;
int nonmesh_prim_id_index(pxr::SdfPath const &id) const;
void update_nonmesh_instance(NonmeshInstance &inst);

View File

@ -19,12 +19,12 @@
#include "hydra_scene_delegate.h"
#include "mesh.h"
PXR_NAMESPACE_OPEN_SCOPE
TF_DEFINE_PRIVATE_TOKENS(tokens_, (st));
PXR_NAMESPACE_CLOSE_SCOPE
namespace blender::io::hydra {
namespace usdtokens {
static const pxr::TfToken st("st", pxr::TfToken::Immortal);
}
MeshData::MeshData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id)
@ -99,7 +99,7 @@ pxr::VtValue MeshData::get_data(pxr::SdfPath const &id, pxr::TfToken const &key)
if (key == pxr::HdTokens->normals) {
return pxr::VtValue(submesh(id).normals);
}
if (key == pxr::tokens_->st) {
if (key == usdtokens::st) {
return pxr::VtValue(submesh(id).uvs);
}
if (key == pxr::HdTokens->points) {
@ -150,7 +150,7 @@ pxr::HdPrimvarDescriptorVector MeshData::primvar_descriptors(
}
if (!submeshes_[0].uvs.empty()) {
primvars.emplace_back(
pxr::tokens_->st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
usdtokens::st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
}
}
return primvars;

View File

@ -17,10 +17,6 @@
#include "hydra_scene_delegate.h"
PXR_NAMESPACE_OPEN_SCOPE
TF_DEFINE_PRIVATE_TOKENS(grid_tokens_, (density)(flame)(shadow)(temperature)(velocity));
PXR_NAMESPACE_CLOSE_SCOPE
namespace blender::io::hydra {
VolumeModifierData::VolumeModifierData(HydraSceneDelegate *scene_delegate,
@ -62,7 +58,13 @@ void VolumeModifierData::init()
scene_delegate_->scene->r.cfra);
ID_LOG(1, "%s", filepath_.c_str());
for (auto &grid_name : pxr::grid_tokens_->allTokens) {
static const pxr::TfToken grid_tokens[] = {pxr::TfToken("density", pxr::TfToken::Immortal),
pxr::TfToken("flame", pxr::TfToken::Immortal),
pxr::TfToken("shadow", pxr::TfToken::Immortal),
pxr::TfToken("temperature", pxr::TfToken::Immortal),
pxr::TfToken("velocity", pxr::TfToken::Immortal)};
for (const auto &grid_name : grid_tokens) {
field_descriptors_.emplace_back(grid_name,
pxr::UsdVolImagingTokens->openvdbAsset,
prim_id.AppendElementString("VF_" + grid_name.GetString()));