Blender  V2.93
abc_writer_hair.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include "abc_writer_hair.h"
23 
24 #include <cstdio>
25 
26 #include "DNA_mesh_types.h"
27 #include "DNA_meshdata_types.h"
28 #include "DNA_modifier_types.h"
29 #include "DNA_object_types.h"
30 
31 #include "BLI_math_geom.h"
32 
33 #include "BKE_customdata.h"
34 #include "BKE_mesh.h"
35 #include "BKE_mesh_runtime.h"
36 #include "BKE_particle.h"
37 
38 #include "CLG_log.h"
39 static CLG_LogRef LOG = {"io.alembic"};
40 
41 using Alembic::Abc::P3fArraySamplePtr;
42 using Alembic::AbcGeom::OCurves;
43 using Alembic::AbcGeom::OCurvesSchema;
44 using Alembic::AbcGeom::ON3fGeomParam;
45 using Alembic::AbcGeom::OV2fGeomParam;
46 
47 namespace blender::io::alembic {
48 
50  : ABCAbstractWriter(args), uv_warning_shown_(false)
51 {
52 }
53 
55 {
56  CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
57  abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
58  abc_curves_schema_ = abc_curves_.getSchema();
59 }
60 
61 Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
62 {
63  return abc_curves_;
64 }
65 
66 Alembic::Abc::OCompoundProperty ABCHairWriter::abc_prop_for_custom_props()
67 {
68  return abc_schema_prop_for_custom_props(abc_curves_schema_);
69 }
70 
71 bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
72 {
73  /* We assume that hair particles are always animated. */
74  return true;
75 }
76 
78 {
82 
83  std::vector<Imath::V3f> verts;
84  std::vector<int32_t> hvertices;
85  std::vector<Imath::V2f> uv_values;
86  std::vector<Imath::V3f> norm_values;
87 
88  ParticleSystem *psys = context.particle_system;
89  if (psys->pathcache) {
90  ParticleSettings *part = psys->part;
91  bool export_children = psys->childcache && part->childtype != 0;
92 
93  if (!export_children || part->draw & PART_DRAW_PARENT) {
94  write_hair_sample(context, mesh, verts, norm_values, uv_values, hvertices);
95  }
96 
97  if (export_children) {
98  write_hair_child_sample(context, mesh, verts, norm_values, uv_values, hvertices);
99  }
100  }
101 
102  Alembic::Abc::P3fArraySample iPos(verts);
103  OCurvesSchema::Sample sample(iPos, hvertices);
104  sample.setBasis(Alembic::AbcGeom::kNoBasis);
105  sample.setType(Alembic::AbcGeom::kLinear);
106  sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
107 
108  if (!uv_values.empty()) {
109  OV2fGeomParam::Sample uv_smp;
110  uv_smp.setVals(uv_values);
111  sample.setUVs(uv_smp);
112  }
113 
114  if (!norm_values.empty()) {
115  ON3fGeomParam::Sample norm_smp;
116  norm_smp.setVals(norm_values);
117  sample.setNormals(norm_smp);
118  }
119 
121  sample.setSelfBounds(bounding_box_);
122  abc_curves_schema_.set(sample);
123 }
124 
125 void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
126  Mesh *mesh,
127  std::vector<Imath::V3f> &verts,
128  std::vector<Imath::V3f> &norm_values,
129  std::vector<Imath::V2f> &uv_values,
130  std::vector<int32_t> &hvertices)
131 {
132  /* Get untransformed vertices, there's a xform under the hair. */
133  float inv_mat[4][4];
134  invert_m4_m4_safe(inv_mat, context.object->obmat);
135 
136  MTFace *mtface = mesh->mtface;
137  MFace *mface = mesh->mface;
138  MVert *mverts = mesh->mvert;
139 
140  if ((!mtface || !mface) && !uv_warning_shown_) {
141  std::fprintf(stderr,
142  "Warning, no UV set found for underlying geometry of %s.\n",
143  context.object->id.name + 2);
144  uv_warning_shown_ = true;
145  }
146 
147  ParticleSystem *psys = context.particle_system;
148  ParticleSettings *part = psys->part;
149  ParticleData *pa = psys->particles;
150  int k;
151 
152  ParticleCacheKey **cache = psys->pathcache;
153  ParticleCacheKey *path;
154  float normal[3];
155  Imath::V3f tmp_nor;
156 
157  for (int p = 0; p < psys->totpart; p++, pa++) {
158  /* underlying info for faces-only emission */
159  path = cache[p];
160 
161  /* Write UV and normal vectors */
162  if (part->from == PART_FROM_FACE && mtface) {
163  const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num;
164 
165  if (num < mesh->totface) {
166  /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */
167  MFace *face = mface == nullptr ? nullptr : &mface[num];
168  MTFace *tface = mtface + num;
169 
170  if (mface) {
171  float r_uv[2], mapfw[4], vec[3];
172 
173  psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv);
174  uv_values.emplace_back(r_uv[0], r_uv[1]);
175 
177  mverts, face, tface, nullptr, mapfw, vec, normal, nullptr, nullptr, nullptr);
178 
179  copy_yup_from_zup(tmp_nor.getValue(), normal);
180  norm_values.push_back(tmp_nor);
181  }
182  }
183  else {
184  std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface);
185  }
186  }
187  else if (part->from == PART_FROM_VERT && mtface) {
188  /* vertex id */
189  const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num;
190 
191  /* iterate over all faces to find a corresponding underlying UV */
192  for (int n = 0; n < mesh->totface; n++) {
193  MFace *face = &mface[n];
194  MTFace *tface = mtface + n;
195  unsigned int vtx[4];
196  vtx[0] = face->v1;
197  vtx[1] = face->v2;
198  vtx[2] = face->v3;
199  vtx[3] = face->v4;
200  bool found = false;
201 
202  for (int o = 0; o < 4; o++) {
203  if (o > 2 && vtx[o] == 0) {
204  break;
205  }
206 
207  if (vtx[o] == num) {
208  uv_values.emplace_back(tface->uv[o][0], tface->uv[o][1]);
209 
210  MVert *mv = mverts + vtx[o];
211 
213  copy_yup_from_zup(tmp_nor.getValue(), normal);
214  norm_values.push_back(tmp_nor);
215  found = true;
216  break;
217  }
218  }
219 
220  if (found) {
221  break;
222  }
223  }
224  }
225 
226  int steps = path->segments + 1;
227  hvertices.push_back(steps);
228 
229  for (k = 0; k < steps; k++, path++) {
230  float vert[3];
231  copy_v3_v3(vert, path->co);
232  mul_m4_v3(inv_mat, vert);
233 
234  /* Convert Z-up to Y-up. */
235  verts.emplace_back(vert[0], vert[2], -vert[1]);
236  }
237  }
238 }
239 
240 void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
241  Mesh *mesh,
242  std::vector<Imath::V3f> &verts,
243  std::vector<Imath::V3f> &norm_values,
244  std::vector<Imath::V2f> &uv_values,
245  std::vector<int32_t> &hvertices)
246 {
247  /* Get untransformed vertices, there's a xform under the hair. */
248  float inv_mat[4][4];
249  invert_m4_m4_safe(inv_mat, context.object->obmat);
250 
251  MTFace *mtface = mesh->mtface;
252  MVert *mverts = mesh->mvert;
253 
254  ParticleSystem *psys = context.particle_system;
255  ParticleSettings *part = psys->part;
256  ParticleCacheKey **cache = psys->childcache;
257  ParticleCacheKey *path;
258 
259  ChildParticle *pc = psys->child;
260 
261  for (int p = 0; p < psys->totchild; p++, pc++) {
262  path = cache[p];
263 
264  if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
265  const int num = pc->num;
266  if (num < 0) {
267  CLOG_WARN(
268  &LOG,
269  "Child particle of hair system %s has unknown face index of geometry of %s, skipping "
270  "child hair.",
271  psys->name,
272  context.object->id.name + 2);
273  continue;
274  }
275 
276  MFace *face = &mesh->mface[num];
277  MTFace *tface = mtface + num;
278 
279  float r_uv[2], tmpnor[3], mapfw[4], vec[3];
280 
281  psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv);
282  uv_values.emplace_back(r_uv[0], r_uv[1]);
283 
285  mverts, face, tface, nullptr, mapfw, vec, tmpnor, nullptr, nullptr, nullptr);
286 
287  /* Convert Z-up to Y-up. */
288  norm_values.emplace_back(tmpnor[0], tmpnor[2], -tmpnor[1]);
289  }
290  else {
291  if (!uv_values.empty()) {
292  uv_values.push_back(uv_values[pc->parent]);
293  }
294  if (!norm_values.empty()) {
295  norm_values.push_back(norm_values[pc->parent]);
296  }
297  }
298 
299  int steps = path->segments + 1;
300  hvertices.push_back(steps);
301 
302  for (int k = 0; k < steps; k++) {
303  float vert[3];
304  copy_v3_v3(vert, path->co);
305  mul_m4_v3(inv_mat, vert);
306 
307  /* Convert Z-up to Y-up. */
308  verts.emplace_back(vert[0], vert[2], -vert[1]);
309 
310  path++;
311  }
312  }
313 }
314 
315 } // namespace blender::io::alembic
CustomData interface, see also DNA_customdata_types.h.
const CustomData_MeshMasks CD_MASK_MESH
Definition: customdata.c:1933
void BKE_mesh_tessface_ensure(struct Mesh *mesh)
Definition: mesh.c:1560
struct Mesh * mesh_get_eval_final(struct Depsgraph *depsgraph, 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 MVert *mvert, struct MFace *mface, struct MTFace *tface, float(*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3], float orco[3])
Definition: particle.c:1670
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:732
void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
Definition: math_matrix.c:3246
MINLINE void normal_short_to_float_v3(float r[3], const short n[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define CLOG_WARN(clg_ref,...)
Definition: CLG_log.h:203
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:201
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
static void sample(SocketReader *reader, int x, int y, float color[4])
BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
struct SELECTID_Context context
Definition: select_engine.c:47
static const int steps
Definition: sky_nishita.cpp:28
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