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
7 changed files with 293 additions and 185 deletions
Showing only changes of commit fddf4a3186 - Show all commits

View File

@ -152,6 +152,7 @@ if(WITH_HYDRA)
list(APPEND SRC
hydra/camera.cc
hydra/curves.cc
hydra/hair.cc
hydra/hydra_scene_delegate.cc
hydra/id.cc
hydra/image.cc
@ -168,6 +169,7 @@ if(WITH_HYDRA)
hydra/camera.h
hydra/curves.h
hydra/hair.h
hydra/hydra_scene_delegate.h
hydra/id.h
hydra/image.h

View File

@ -0,0 +1,157 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "curves.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/tokens.h>
#include "BLI_string.h"
#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_curves.hh"
#include "DEG_depsgraph_query.hh"
#include "hair.h"
#include "hydra_scene_delegate.h"
namespace blender::io::hydra {
HairData::HairData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system)
: ParticleSystemData(scene_delegate, object, prim_id), particle_system(particle_system)
{
}
void HairData::init()
{
ID_LOGN(1, "");
/* NOTE: no need to write_transform here, since we already write actual position. */
write_hair();
write_materials();
}
void HairData::insert()
{
ID_LOGN(1, "");
scene_delegate_->GetRenderIndex().InsertRprim(
pxr::HdPrimTypeTokens->basisCurves, scene_delegate_, prim_id);
}
void HairData::remove()
{
ID_LOG(1, "");
curve_vertex_counts_.clear();
vertices_.clear();
scene_delegate_->GetRenderIndex().RemoveRprim(prim_id);
}
void HairData::update()
{
init();
switch (particle_system->part->type) {
case PART_HAIR:
if (!scene_delegate_->GetRenderIndex().HasRprim(prim_id)) {
insert();
}
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(prim_id, pxr::HdChangeTracker::AllDirty);
break;
case PART_EMITTER:
case PART_FLUID_FLIP:
case PART_FLUID_SPRAY:
case PART_FLUID_BUBBLE:
case PART_FLUID_FOAM:
case PART_FLUID_TRACER:
case PART_FLUID_SPRAYFOAM:
case PART_FLUID_SPRAYBUBBLE:
case PART_FLUID_FOAMBUBBLE:
case PART_FLUID_SPRAYFOAMBUBBLE:
CLOG_WARN(LOG_HYDRA_SCENE, "Unsupported particle type: %d", particle_system->part->type);
remove();
break;
default:
BLI_assert_unreachable();
}
ID_LOGN(1, "");
}
pxr::VtValue HairData::get_data(pxr::TfToken const &key) const
{
if (key == pxr::HdTokens->points) {
return pxr::VtValue(vertices_);
}
return pxr::VtValue();
}
pxr::SdfPath HairData::material_id(pxr::SdfPath const &id) const
{
if (!mat_data_) {
return pxr::SdfPath();
}
return mat_data_->prim_id;
}
pxr::HdBasisCurvesTopology HairData::topology() const
{
return pxr::HdBasisCurvesTopology(pxr::HdTokens->cubic,
pxr::UsdGeomTokens->bspline,
pxr::HdTokens->nonperiodic,
curve_vertex_counts_,
pxr::VtIntArray());
}
pxr::HdPrimvarDescriptorVector HairData::primvar_descriptors(
pxr::HdInterpolation interpolation) const
{
pxr::HdPrimvarDescriptorVector primvars;
if (interpolation == pxr::HdInterpolationVertex) {
if (!vertices_.empty()) {
primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
}
}
return primvars;
}
void HairData::write_materials()
{
const Object *object = (const Object *)id;
const Material *mat = nullptr;
/* TODO: Using only first material. Add support for multi-material. */
if (BKE_object_material_count_eval(object) > 0) {
mat = BKE_object_material_get_eval(const_cast<Object *>(object), 0);
}
mat_data_ = get_or_create_material(mat);
}
void HairData::write_hair()
{
ParticleCacheKey **cache = particle_system->pathcache;
if (cache == nullptr) {
return;
}
vertices_.clear();
curve_vertex_counts_.clear();
vertices_.reserve(particle_system->totpart);
curve_vertex_counts_.reserve(particle_system->totpart);
ParticleCacheKey *strand;
for (int strand_index = 0; strand_index < particle_system->totpart; ++strand_index) {
strand = cache[strand_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));
}
}
}
} // namespace blender::io::hydra

View File

@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include "BLI_set.hh"
#include "BKE_duplilist.h"
#include "BKE_particle.h"
#include "DNA_particle_types.h"
#include "material.h"
#include "particle_system.h"
namespace blender::io::hydra {
class HairData : public ParticleSystemData {
private:
pxr::VtIntArray curve_vertex_counts_;
pxr::VtVec3fArray vertices_;
MaterialData *mat_data_ = nullptr;
public:
HairData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system);
void init() override;
void insert() override;
void remove() override;
void update() override;
pxr::VtValue get_data(pxr::TfToken const &key) const override;
pxr::SdfPath material_id(pxr::SdfPath const &id) const override;
pxr::HdBasisCurvesTopology topology() const;
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
ParticleSystem *particle_system;
protected:
void write_materials() override;
private:
void write_hair();
};
} // namespace blender::io::hydra

View File

@ -48,9 +48,9 @@ pxr::HdBasisCurvesTopology HydraSceneDelegate::GetBasisCurvesTopology(pxr::SdfPa
{
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
auto p_data = particle_system_data(id);
if (p_data) {
return p_data->topology();
auto h_data = hair_data(id);
if (h_data) {
return h_data->topology();
}
CurvesData *c_data = curves_data(id);
@ -105,9 +105,9 @@ pxr::HdPrimvarDescriptorVector HydraSceneDelegate::GetPrimvarDescriptors(
{
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s, %d", id.GetText(), interpolation);
ParticleSystemData *p_data = particle_system_data(id);
if (p_data) {
return p_data->primvar_descriptors(interpolation);
HairData *h_data = hair_data(id);
if (h_data) {
return h_data->primvar_descriptors(interpolation);
}
MeshData *m_data = mesh_data(id);
if (m_data) {
@ -352,6 +352,11 @@ ParticleSystemData *HydraSceneDelegate::particle_system_data(pxr::SdfPath const
return psys_data->get();
}
HairData *HydraSceneDelegate::hair_data(pxr::SdfPath const &id) const
{
return dynamic_cast<HairData *>(particle_system_data(id));
}
InstancerData *HydraSceneDelegate::instancer_data(pxr::SdfPath const &id, bool child_id) const
{
pxr::SdfPath p_id;
@ -574,7 +579,8 @@ void HydraSceneDelegate::update_particle_systems()
for (auto &obj : objects_.values()) {
Object *object = (Object *)obj->id;
LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
if (ParticleSystemData::is_visible(this, object, psys)) {
if (ParticleSystemData::is_supported(psys)
&& ParticleSystemData::is_visible(this, object, psys)) {
pxr::SdfPath psys_path = particle_system_prim_id(obj->prim_id, psys);
ParticleSystemData *psys_data = particle_systems_.contains(psys_path) ?
particle_systems_.lookup_ptr(psys_path)->get() :
@ -582,11 +588,10 @@ void HydraSceneDelegate::update_particle_systems()
if (!psys_data) {
psys_data = particle_systems_
.lookup_or_add(
psys_path,
std::make_unique<ParticleSystemData>(this, object, psys_path, psys))
.lookup_or_add(psys_path,
ParticleSystemData::create(
this, object, psys_path, psys))
.get();
psys_data->init();
psys_data->insert();
}
else {
@ -597,7 +602,8 @@ void HydraSceneDelegate::update_particle_systems()
}
particle_systems_.remove_if([&](auto item) {
Object *object = (Object *)item.value->id;
ParticleSystem *psys = item.value->particle_system;
HairData *h_data = hair_data(item.value->prim_id);
ParticleSystem *psys = h_data->particle_system;
bool ret = ParticleSystemData::is_supported(psys);
if (!ret || !ParticleSystemData::is_visible(this, object, psys)) {
item.value->remove();

View File

@ -14,6 +14,7 @@
#include "CLG_log.h"
#include "curves.h"
#include "hair.h"
#include "instancer.h"
#include "light.h"
#include "mesh.h"
@ -109,6 +110,7 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
LightData *light_data(pxr::SdfPath const &id) const;
MaterialData *material_data(pxr::SdfPath const &id) const;
ParticleSystemData *particle_system_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 update_world();

View File

@ -2,8 +2,6 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "curves.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/tokens.h>
@ -16,18 +14,56 @@
#include "DEG_depsgraph_query.hh"
#include "particle_system.h"
#include "hair.h"
#include "hydra_scene_delegate.h"
namespace blender::io::hydra {
ParticleSystemData::ParticleSystemData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system)
: ObjectData(scene_delegate, object, prim_id), particle_system(particle_system)
pxr::SdfPath const &prim_id)
: ObjectData(scene_delegate, object, prim_id)
{
}
std::unique_ptr<ParticleSystemData> ParticleSystemData::create(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system)
{
std::unique_ptr<ParticleSystemData> psys_data;
if (particle_system->part) {
switch (particle_system->part->type) {
case PART_HAIR:
psys_data = std::make_unique<HairData>(scene_delegate,
object,
prim_id,
particle_system);
break;
case PART_EMITTER:
case PART_FLUID_FLIP:
case PART_FLUID_SPRAY:
case PART_FLUID_BUBBLE:
case PART_FLUID_FOAM:
case PART_FLUID_TRACER:
case PART_FLUID_SPRAYFOAM:
case PART_FLUID_SPRAYBUBBLE:
case PART_FLUID_FOAMBUBBLE:
case PART_FLUID_SPRAYFOAMBUBBLE:
CLOG_WARN(LOG_HYDRA_SCENE, "Unsupported particle type: %d", particle_system->part->type);
break;
default:
BLI_assert_unreachable();
}
}
if (psys_data) {
psys_data->init();
}
return psys_data;
}
bool ParticleSystemData::is_supported(const ParticleSystem *particle_system)
{
if (particle_system->part) {
@ -63,151 +99,17 @@ bool ParticleSystemData::is_visible(HydraSceneDelegate *scene_delegate,
psys_check_enabled(object, particle_system, for_render);
}
void ParticleSystemData::init()
pxr::VtValue ParticleSystemData::get_data(pxr::TfToken const & /*key*/) const
{
ID_LOGN(1, "");
/* NOTE: no need to write_transform here, since we already write actual position. */
write_curves();
write_materials();
}
void ParticleSystemData::insert()
{
ID_LOGN(1, "");
scene_delegate_->GetRenderIndex().InsertRprim(
pxr::HdPrimTypeTokens->basisCurves, scene_delegate_, prim_id);
}
void ParticleSystemData::remove()
{
ID_LOG(1, "");
curve_vertex_counts_.clear();
vertices_.clear();
scene_delegate_->GetRenderIndex().RemoveRprim(prim_id);
}
void ParticleSystemData::update()
{
init();
switch (particle_system->part->type) {
case PART_HAIR:
if (!scene_delegate_->GetRenderIndex().HasRprim(prim_id)) {
insert();
}
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(prim_id, pxr::HdChangeTracker::AllDirty);
break;
case PART_EMITTER:
case PART_FLUID_FLIP:
case PART_FLUID_SPRAY:
case PART_FLUID_BUBBLE:
case PART_FLUID_FOAM:
case PART_FLUID_TRACER:
case PART_FLUID_SPRAYFOAM:
case PART_FLUID_SPRAYBUBBLE:
case PART_FLUID_FOAMBUBBLE:
case PART_FLUID_SPRAYFOAMBUBBLE:
CLOG_WARN(LOG_HYDRA_SCENE, "Unsupported particle type: %d", particle_system->part->type);
remove();
break;
default:
BLI_assert_unreachable();
}
ID_LOGN(1, "");
}
pxr::VtValue ParticleSystemData::get_data(pxr::TfToken const &key) const
{
if (key == pxr::HdTokens->points) {
return pxr::VtValue(vertices_);
}
return pxr::VtValue();
}
pxr::SdfPath ParticleSystemData::material_id(pxr::SdfPath const &id) const
{
if (!mat_data_) {
return pxr::SdfPath();
}
return mat_data_->prim_id;
}
void ParticleSystemData::init() {}
pxr::HdBasisCurvesTopology ParticleSystemData::topology() const
{
return pxr::HdBasisCurvesTopology(pxr::HdTokens->cubic,
pxr::UsdGeomTokens->bspline,
pxr::HdTokens->nonperiodic,
curve_vertex_counts_,
pxr::VtIntArray());
}
void ParticleSystemData::insert() {}
pxr::HdPrimvarDescriptorVector ParticleSystemData::primvar_descriptors(
pxr::HdInterpolation interpolation) const
{
pxr::HdPrimvarDescriptorVector primvars;
if (interpolation == pxr::HdInterpolationVertex) {
if (!vertices_.empty()) {
primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
}
}
return primvars;
}
void ParticleSystemData::remove() {}
void ParticleSystemData::write_materials()
{
const Object *object = (const Object *)id;
const Material *mat = nullptr;
/* TODO: Using only first material. Add support for multi-material. */
if (BKE_object_material_count_eval(object) > 0) {
mat = BKE_object_material_get_eval(const_cast<Object *>(object), 0);
}
mat_data_ = get_or_create_material(mat);
}
void ParticleSystemData::write_curves() {
switch (particle_system->part->type) {
case PART_HAIR:
write_hair();
break;
case PART_EMITTER:
case PART_FLUID_FLIP:
case PART_FLUID_SPRAY:
case PART_FLUID_BUBBLE:
case PART_FLUID_FOAM:
case PART_FLUID_TRACER:
case PART_FLUID_SPRAYFOAM:
case PART_FLUID_SPRAYBUBBLE:
case PART_FLUID_FOAMBUBBLE:
case PART_FLUID_SPRAYFOAMBUBBLE:
CLOG_WARN(LOG_HYDRA_SCENE, "Unsupported particle type: %d", particle_system->part->type);
break;
default:
BLI_assert_unreachable();
}
}
void ParticleSystemData::write_hair()
{
ParticleCacheKey **cache = particle_system->pathcache;
if (cache == nullptr) {
return;
}
vertices_.clear();
curve_vertex_counts_.clear();
vertices_.reserve(particle_system->totpart);
curve_vertex_counts_.reserve(particle_system->totpart);
ParticleCacheKey *strand;
for (int strand_index = 0; strand_index < particle_system->totpart; ++strand_index) {
strand = cache[strand_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));
}
}
}
void ParticleSystemData::update() {}
} // namespace blender::io::hydra

View File

@ -20,18 +20,15 @@
namespace blender::io::hydra {
class ParticleSystemData : public ObjectData {
private:
pxr::VtIntArray curve_vertex_counts_;
pxr::VtVec3fArray vertices_;
MaterialData *mat_data_ = nullptr;
public:
ParticleSystemData(HydraSceneDelegate *scene_delegate,
const Object *object,
pxr::SdfPath const &prim_id,
ParticleSystem *particle_system);
const Object *object,
pxr::SdfPath const &prim_id);
static std::unique_ptr<ParticleSystemData> create(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,
@ -39,25 +36,11 @@ class ParticleSystemData : public ObjectData {
ParticleSystem *particle_system,
int object_mode = OB_VISIBLE_SELF);
void init() override;
void insert() override;
void remove() override;
void update() override;
pxr::VtValue get_data(pxr::TfToken const &key) const override;
pxr::SdfPath material_id(pxr::SdfPath const &id) const override;
pxr::HdBasisCurvesTopology topology() const;
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
ParticleSystem *particle_system;
protected:
void write_materials() override;
private:
void write_curves();
void write_hair();
virtual pxr::VtValue get_data(pxr::TfToken const &key) const override;
virtual void init() override;
virtual void insert() override;
virtual void remove() override;
virtual void update() override;
};
using ParticleSystemMap = Map<pxr::SdfPath, std::unique_ptr<ParticleSystemData>>;