Blender  V3.3
abc_writer_hair.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "abc_writer_hair.h"
9 
10 #include <cstdio>
11 
12 #include "DNA_mesh_types.h"
13 #include "DNA_meshdata_types.h"
14 #include "DNA_modifier_types.h"
15 #include "DNA_object_types.h"
16 
17 #include "BLI_math_geom.h"
18 
19 #include "BKE_customdata.h"
20 #include "BKE_mesh.h"
22 #include "BKE_mesh_runtime.h"
23 #include "BKE_particle.h"
24 
25 #include "CLG_log.h"
26 static CLG_LogRef LOG = {"io.alembic"};
27 
28 using Alembic::Abc::P3fArraySamplePtr;
29 using Alembic::AbcGeom::OCurves;
30 using Alembic::AbcGeom::OCurvesSchema;
31 using Alembic::AbcGeom::ON3fGeomParam;
32 using Alembic::AbcGeom::OV2fGeomParam;
33 
34 namespace blender::io::alembic {
35 
37  : ABCAbstractWriter(args), uv_warning_shown_(false)
38 {
39 }
40 
42 {
43  CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
44  abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
45  abc_curves_schema_ = abc_curves_.getSchema();
46 }
47 
48 Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
49 {
50  return abc_curves_;
51 }
52 
53 Alembic::Abc::OCompoundProperty ABCHairWriter::abc_prop_for_custom_props()
54 {
55  return abc_schema_prop_for_custom_props(abc_curves_schema_);
56 }
57 
58 bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
59 {
60  /* We assume that hair particles are always animated. */
61  return true;
62 }
63 
65 {
69 
70  std::vector<Imath::V3f> verts;
71  std::vector<int32_t> hvertices;
72  std::vector<Imath::V2f> uv_values;
73  std::vector<Imath::V3f> norm_values;
74 
75  ParticleSystem *psys = context.particle_system;
76  if (psys->pathcache) {
77  ParticleSettings *part = psys->part;
78  bool export_children = psys->childcache && part->childtype != 0;
79 
80  if (!export_children || part->draw & PART_DRAW_PARENT) {
81  write_hair_sample(context, mesh, verts, norm_values, uv_values, hvertices);
82  }
83 
84  if (export_children) {
85  write_hair_child_sample(context, mesh, verts, norm_values, uv_values, hvertices);
86  }
87  }
88 
89  Alembic::Abc::P3fArraySample iPos(verts);
90  OCurvesSchema::Sample sample(iPos, hvertices);
91  sample.setBasis(Alembic::AbcGeom::kNoBasis);
92  sample.setType(Alembic::AbcGeom::kLinear);
93  sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
94 
95  if (!uv_values.empty()) {
96  OV2fGeomParam::Sample uv_smp;
97  uv_smp.setVals(uv_values);
98  sample.setUVs(uv_smp);
99  }
100 
101  if (!norm_values.empty()) {
102  ON3fGeomParam::Sample norm_smp;
103  norm_smp.setVals(norm_values);
104  sample.setNormals(norm_smp);
105  }
106 
108  sample.setSelfBounds(bounding_box_);
109  abc_curves_schema_.set(sample);
110 }
111 
112 void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
113  Mesh *mesh,
114  std::vector<Imath::V3f> &verts,
115  std::vector<Imath::V3f> &norm_values,
116  std::vector<Imath::V2f> &uv_values,
117  std::vector<int32_t> &hvertices)
118 {
119  /* Get untransformed vertices, there's a xform under the hair. */
120  float inv_mat[4][4];
121  invert_m4_m4_safe(inv_mat, context.object->obmat);
122 
123  MTFace *mtface = mesh->mtface;
124  MFace *mface = mesh->mface;
125  MVert *mverts = mesh->mvert;
126  const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
127 
128  if ((!mtface || !mface) && !uv_warning_shown_) {
129  std::fprintf(stderr,
130  "Warning, no UV set found for underlying geometry of %s.\n",
131  context.object->id.name + 2);
132  uv_warning_shown_ = true;
133  }
134 
135  ParticleSystem *psys = context.particle_system;
136  ParticleSettings *part = psys->part;
137  ParticleData *pa = psys->particles;
138  int k;
139 
140  ParticleCacheKey **cache = psys->pathcache;
141  ParticleCacheKey *path;
142  float normal[3];
143  Imath::V3f tmp_nor;
144 
145  for (int p = 0; p < psys->totpart; p++, pa++) {
146  /* underlying info for faces-only emission */
147  path = cache[p];
148 
149  /* Write UV and normal vectors */
150  if (part->from == PART_FROM_FACE && mtface) {
151  const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num;
152 
153  if (num < mesh->totface) {
154  /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */
155  MFace *face = mface == nullptr ? nullptr : &mface[num];
156  MTFace *tface = mtface + num;
157 
158  if (mface) {
159  float r_uv[2], mapfw[4], vec[3];
160 
161  psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv);
162  uv_values.emplace_back(r_uv[0], r_uv[1]);
163 
165  mverts,
166  vert_normals,
167  face,
168  tface,
169  nullptr,
170  mapfw,
171  vec,
172  normal,
173  nullptr,
174  nullptr,
175  nullptr);
176 
177  copy_yup_from_zup(tmp_nor.getValue(), normal);
178  norm_values.push_back(tmp_nor);
179  }
180  }
181  else {
182  std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface);
183  }
184  }
185  else if (part->from == PART_FROM_VERT && mtface) {
186  /* vertex id */
187  const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num;
188 
189  /* iterate over all faces to find a corresponding underlying UV */
190  for (int n = 0; n < mesh->totface; n++) {
191  MFace *face = &mface[n];
192  MTFace *tface = mtface + n;
193  unsigned int vtx[4];
194  vtx[0] = face->v1;
195  vtx[1] = face->v2;
196  vtx[2] = face->v3;
197  vtx[3] = face->v4;
198  bool found = false;
199 
200  for (int o = 0; o < 4; o++) {
201  if (o > 2 && vtx[o] == 0) {
202  break;
203  }
204 
205  if (vtx[o] == num) {
206  uv_values.emplace_back(tface->uv[o][0], tface->uv[o][1]);
207  copy_v3_v3(normal, vert_normals[vtx[o]]);
208  copy_yup_from_zup(tmp_nor.getValue(), normal);
209  norm_values.push_back(tmp_nor);
210  found = true;
211  break;
212  }
213  }
214 
215  if (found) {
216  break;
217  }
218  }
219  }
220 
221  int steps = path->segments + 1;
222  hvertices.push_back(steps);
223 
224  for (k = 0; k < steps; k++, path++) {
225  float vert[3];
226  copy_v3_v3(vert, path->co);
227  mul_m4_v3(inv_mat, vert);
228 
229  /* Convert Z-up to Y-up. */
230  verts.emplace_back(vert[0], vert[2], -vert[1]);
231  }
232  }
233 }
234 
235 void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
236  Mesh *mesh,
237  std::vector<Imath::V3f> &verts,
238  std::vector<Imath::V3f> &norm_values,
239  std::vector<Imath::V2f> &uv_values,
240  std::vector<int32_t> &hvertices)
241 {
242  /* Get untransformed vertices, there's a xform under the hair. */
243  float inv_mat[4][4];
244  invert_m4_m4_safe(inv_mat, context.object->obmat);
245 
246  MTFace *mtface = mesh->mtface;
247  MVert *mverts = mesh->mvert;
248  const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
249 
250  ParticleSystem *psys = context.particle_system;
251  ParticleSettings *part = psys->part;
252  ParticleCacheKey **cache = psys->childcache;
253  ParticleCacheKey *path;
254 
255  ChildParticle *pc = psys->child;
256 
257  for (int p = 0; p < psys->totchild; p++, pc++) {
258  path = cache[p];
259 
260  if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
261  const int num = pc->num;
262  if (num < 0) {
263  CLOG_WARN(
264  &LOG,
265  "Child particle of hair system %s has unknown face index of geometry of %s, skipping "
266  "child hair.",
267  psys->name,
268  context.object->id.name + 2);
269  continue;
270  }
271 
272  MFace *face = &mesh->mface[num];
273  MTFace *tface = mtface + num;
274 
275  float r_uv[2], tmpnor[3], mapfw[4], vec[3];
276 
277  psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv);
278  uv_values.emplace_back(r_uv[0], r_uv[1]);
279 
281  mverts,
282  vert_normals,
283  face,
284  tface,
285  nullptr,
286  mapfw,
287  vec,
288  tmpnor,
289  nullptr,
290  nullptr,
291  nullptr);
292 
293  /* Convert Z-up to Y-up. */
294  norm_values.emplace_back(tmpnor[0], tmpnor[2], -tmpnor[1]);
295  }
296  else {
297  if (!uv_values.empty()) {
298  uv_values.push_back(uv_values[pc->parent]);
299  }
300  if (!norm_values.empty()) {
301  norm_values.push_back(norm_values[pc->parent]);
302  }
303  }
304 
305  int steps = path->segments + 1;
306  hvertices.push_back(steps);
307 
308  for (int k = 0; k < steps; k++) {
309  float vert[3];
310  copy_v3_v3(vert, path->co);
311  mul_m4_v3(inv_mat, vert);
312 
313  /* Convert Z-up to Y-up. */
314  verts.emplace_back(vert[0], vert[2], -vert[1]);
315 
316  path++;
317  }
318  }
319 }
320 
321 } // namespace blender::io::alembic
typedef float(TangentPoint)[2]
CustomData interface, see also DNA_customdata_types.h.
const CustomData_MeshMasks CD_MASK_MESH
Definition: customdata.cc:2065
const float(* BKE_mesh_vertex_normals_ensure(const struct Mesh *mesh))[3]
void BKE_mesh_tessface_ensure(struct Mesh *mesh)
struct Mesh * mesh_get_eval_final(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, const struct CustomData_MeshMasks *dataMask)
void psys_interpolate_uvs(const struct MTFace *tface, int quad, const float w[4], float uvco[2])
void psys_interpolate_face(struct Mesh *mesh, struct MVert *mvert, const float(*vert_normals)[3], struct MFace *mface, struct MTFace *tface, const float(*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3], float orco[3])
Definition: particle.c:1689
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
Definition: math_matrix.c:3153
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define CLOG_WARN(clg_ref,...)
Definition: CLG_log.h:189
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
struct Scene * DEG_get_evaluated_scene(const struct Depsgraph *graph)
Object is a sort of wrapper for general info.
#define PART_FROM_FACE
#define PART_FROM_VERT
#define PART_CHILD_PARTICLES
@ PART_DRAW_PARENT
static CLG_LogRef LOG
Alembic::Abc::OCompoundProperty abc_schema_prop_for_custom_props(T abc_schema)
const ABCWriterConstructorArgs args_
virtual void update_bounding_box(Object *object)
virtual Alembic::Abc::OObject get_alembic_object() const override
ABCHairWriter(const ABCWriterConstructorArgs &args)
virtual void do_write(HierarchyContext &context) override
virtual bool check_is_animated(const HierarchyContext &context) const override
Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override
virtual void create_alembic_objects(const HierarchyContext *context) override
static float verts[][3]
IconTextureDrawCall normal
BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
static const int steps
Definition: sky_nishita.cpp:19
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
float uv[4][2]
struct MTFace * mtface
struct MVert * mvert
int totface
struct MFace * mface
ChildParticle * child
ParticleData * particles
ParticleSettings * part
struct ParticleCacheKey ** childcache
struct ParticleCacheKey ** pathcache