Blender  V3.3
node_shader_tex_musgrave.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2005 Blender Foundation. All rights reserved. */
3 
4 #include "node_shader_util.hh"
5 
6 #include "BLI_noise.hh"
7 
8 #include "UI_interface.h"
9 #include "UI_resources.h"
10 
12 
14 
16 {
17  b.is_function_node();
18  b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field();
19  b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) {
20  /* Default to 1 instead of 4, because it is much faster. */
21  node_storage(node).dimensions = 1;
22  });
23  b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f);
24  b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f);
25  b.add_input<decl::Float>(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f);
26  b.add_input<decl::Float>(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f);
27  b.add_input<decl::Float>(N_("Offset")).min(-1000.0f).max(1000.0f);
28  b.add_input<decl::Float>(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f);
29  b.add_output<decl::Float>(N_("Fac")).no_muted_links();
30 }
31 
33 {
34  uiItemR(layout, ptr, "musgrave_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
35  uiItemR(layout, ptr, "musgrave_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
36 }
37 
39 {
40  NodeTexMusgrave *tex = MEM_cnew<NodeTexMusgrave>(__func__);
42  BKE_texture_colormapping_default(&tex->base.color_mapping);
43  tex->musgrave_type = SHD_MUSGRAVE_FBM;
44  tex->dimensions = 3;
45 
46  node->storage = tex;
47 }
48 
49 static const char *gpu_shader_name_get(const int type, const int dimensions)
50 {
51  BLI_assert(type >= 0 && type < 5);
52  BLI_assert(dimensions > 0 && dimensions < 5);
53 
54  switch (type) {
56  return std::array{"node_tex_musgrave_multi_fractal_1d",
57  "node_tex_musgrave_multi_fractal_2d",
58  "node_tex_musgrave_multi_fractal_3d",
59  "node_tex_musgrave_multi_fractal_4d"}[dimensions - 1];
60  case SHD_MUSGRAVE_FBM:
61  return std::array{"node_tex_musgrave_fBm_1d",
62  "node_tex_musgrave_fBm_2d",
63  "node_tex_musgrave_fBm_3d",
64  "node_tex_musgrave_fBm_4d"}[dimensions - 1];
66  return std::array{"node_tex_musgrave_hybrid_multi_fractal_1d",
67  "node_tex_musgrave_hybrid_multi_fractal_2d",
68  "node_tex_musgrave_hybrid_multi_fractal_3d",
69  "node_tex_musgrave_hybrid_multi_fractal_4d"}[dimensions - 1];
71  return std::array{"node_tex_musgrave_ridged_multi_fractal_1d",
72  "node_tex_musgrave_ridged_multi_fractal_2d",
73  "node_tex_musgrave_ridged_multi_fractal_3d",
74  "node_tex_musgrave_ridged_multi_fractal_4d"}[dimensions - 1];
76  return std::array{"node_tex_musgrave_hetero_terrain_1d",
77  "node_tex_musgrave_hetero_terrain_2d",
78  "node_tex_musgrave_hetero_terrain_3d",
79  "node_tex_musgrave_hetero_terrain_4d"}[dimensions - 1];
80  }
81  return nullptr;
82 }
83 
85  bNode *node,
86  bNodeExecData *UNUSED(execdata),
87  GPUNodeStack *in,
89 {
90  node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
92 
93  NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage;
94  int dimensions = tex->dimensions;
95  int type = tex->musgrave_type;
96 
97  const char *name = gpu_shader_name_get(type, dimensions);
98 
99  return GPU_stack_link(mat, node, name, in, out);
100 }
101 
103 {
104  const NodeTexMusgrave &storage = node_storage(*node);
105 
106  bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector");
107  bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W");
108  bNodeSocket *inOffsetSock = nodeFindSocket(node, SOCK_IN, "Offset");
109  bNodeSocket *inGainSock = nodeFindSocket(node, SOCK_IN, "Gain");
110 
111  nodeSetSocketAvailability(ntree, inVectorSock, storage.dimensions != 1);
112  nodeSetSocketAvailability(ntree, inWSock, storage.dimensions == 1 || storage.dimensions == 4);
114  inOffsetSock,
116  storage.musgrave_type != SHD_MUSGRAVE_FBM);
118  inGainSock,
121 
122  bNodeSocket *outFacSock = nodeFindSocket(node, SOCK_OUT, "Fac");
123  node_sock_label(outFacSock, "Height");
124 }
125 
127  private:
128  const int dimensions_;
129  const int musgrave_type_;
130 
131  public:
132  MusgraveFunction(const int dimensions, const int musgrave_type)
133  : dimensions_(dimensions), musgrave_type_(musgrave_type)
134  {
135  BLI_assert(dimensions >= 1 && dimensions <= 4);
136  BLI_assert(musgrave_type >= 0 && musgrave_type <= 4);
137  static std::array<fn::MFSignature, 20> signatures{
142 
147 
152 
157 
162  };
163  this->set_signature(&signatures[dimensions + musgrave_type * 4 - 1]);
164  }
165 
166  static fn::MFSignature create_signature(const int dimensions, const int musgrave_type)
167  {
168  fn::MFSignatureBuilder signature{"Musgrave"};
169 
170  if (ELEM(dimensions, 2, 3, 4)) {
171  signature.single_input<float3>("Vector");
172  }
173  if (ELEM(dimensions, 1, 4)) {
174  signature.single_input<float>("W");
175  }
176  signature.single_input<float>("Scale");
177  signature.single_input<float>("Detail");
178  signature.single_input<float>("Dimension");
179  signature.single_input<float>("Lacunarity");
180  if (ELEM(musgrave_type,
184  signature.single_input<float>("Offset");
185  }
187  signature.single_input<float>("Gain");
188  }
189 
190  signature.single_output<float>("Fac");
191 
192  return signature.build();
193  }
194 
196  {
197  auto get_vector = [&](int param_index) -> VArray<float3> {
198  return params.readonly_single_input<float3>(param_index, "Vector");
199  };
200  auto get_w = [&](int param_index) -> VArray<float> {
201  return params.readonly_single_input<float>(param_index, "W");
202  };
203  auto get_scale = [&](int param_index) -> VArray<float> {
204  return params.readonly_single_input<float>(param_index, "Scale");
205  };
206  auto get_detail = [&](int param_index) -> VArray<float> {
207  return params.readonly_single_input<float>(param_index, "Detail");
208  };
209  auto get_dimension = [&](int param_index) -> VArray<float> {
210  return params.readonly_single_input<float>(param_index, "Dimension");
211  };
212  auto get_lacunarity = [&](int param_index) -> VArray<float> {
213  return params.readonly_single_input<float>(param_index, "Lacunarity");
214  };
215  auto get_offset = [&](int param_index) -> VArray<float> {
216  return params.readonly_single_input<float>(param_index, "Offset");
217  };
218  auto get_gain = [&](int param_index) -> VArray<float> {
219  return params.readonly_single_input<float>(param_index, "Gain");
220  };
221 
222  auto get_r_factor = [&](int param_index) -> MutableSpan<float> {
223  return params.uninitialized_single_output_if_required<float>(param_index, "Fac");
224  };
225 
226  int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4);
227  const VArray<float> &scale = get_scale(param++);
228  const VArray<float> &detail = get_detail(param++);
229  const VArray<float> &dimension = get_dimension(param++);
230  const VArray<float> &lacunarity = get_lacunarity(param++);
231 
232  switch (musgrave_type_) {
234  MutableSpan<float> r_factor = get_r_factor(param++);
235  const bool compute_factor = !r_factor.is_empty();
236  switch (dimensions_) {
237  case 1: {
238  const VArray<float> &w = get_w(0);
239  if (compute_factor) {
240  for (int64_t i : mask) {
241  const float position = w[i] * scale[i];
242  r_factor[i] = noise::musgrave_multi_fractal(
243  position, dimension[i], lacunarity[i], detail[i]);
244  }
245  }
246  break;
247  }
248  case 2: {
249  const VArray<float3> &vector = get_vector(0);
250  if (compute_factor) {
251  for (int64_t i : mask) {
252  const float3 pxyz = vector[i] * scale[i];
253  const float2 position = float2(pxyz[0], pxyz[1]);
254  r_factor[i] = noise::musgrave_multi_fractal(
255  position, dimension[i], lacunarity[i], detail[i]);
256  }
257  }
258  break;
259  }
260  case 3: {
261  const VArray<float3> &vector = get_vector(0);
262  if (compute_factor) {
263  for (int64_t i : mask) {
264  const float3 position = vector[i] * scale[i];
265  r_factor[i] = noise::musgrave_multi_fractal(
266  position, dimension[i], lacunarity[i], detail[i]);
267  }
268  }
269  break;
270  }
271  case 4: {
272  const VArray<float3> &vector = get_vector(0);
273  const VArray<float> &w = get_w(1);
274  if (compute_factor) {
275  for (int64_t i : mask) {
276  const float3 pxyz = vector[i] * scale[i];
277  const float pw = w[i] * scale[i];
278  const float4 position{pxyz[0], pxyz[1], pxyz[2], pw};
279  r_factor[i] = noise::musgrave_multi_fractal(
280  position, dimension[i], lacunarity[i], detail[i]);
281  }
282  }
283  break;
284  }
285  }
286  break;
287  }
289  const VArray<float> &offset = get_offset(param++);
290  const VArray<float> &gain = get_gain(param++);
291  MutableSpan<float> r_factor = get_r_factor(param++);
292  const bool compute_factor = !r_factor.is_empty();
293  switch (dimensions_) {
294  case 1: {
295  const VArray<float> &w = get_w(0);
296  if (compute_factor) {
297  for (int64_t i : mask) {
298  const float position = w[i] * scale[i];
300  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
301  }
302  }
303  break;
304  }
305  case 2: {
306  const VArray<float3> &vector = get_vector(0);
307  if (compute_factor) {
308  for (int64_t i : mask) {
309  const float3 pxyz = vector[i] * scale[i];
310  const float2 position = float2(pxyz[0], pxyz[1]);
312  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
313  }
314  }
315  break;
316  }
317  case 3: {
318  const VArray<float3> &vector = get_vector(0);
319  if (compute_factor) {
320  for (int64_t i : mask) {
321  const float3 position = vector[i] * scale[i];
323  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
324  }
325  }
326  break;
327  }
328  case 4: {
329  const VArray<float3> &vector = get_vector(0);
330  const VArray<float> &w = get_w(1);
331  if (compute_factor) {
332  for (int64_t i : mask) {
333  const float3 pxyz = vector[i] * scale[i];
334  const float pw = w[i] * scale[i];
335  const float4 position{pxyz[0], pxyz[1], pxyz[2], pw};
337  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
338  }
339  }
340  break;
341  }
342  }
343  break;
344  }
346  const VArray<float> &offset = get_offset(param++);
347  const VArray<float> &gain = get_gain(param++);
348  MutableSpan<float> r_factor = get_r_factor(param++);
349  const bool compute_factor = !r_factor.is_empty();
350  switch (dimensions_) {
351  case 1: {
352  const VArray<float> &w = get_w(0);
353  if (compute_factor) {
354  for (int64_t i : mask) {
355  const float position = w[i] * scale[i];
357  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
358  }
359  }
360  break;
361  }
362  case 2: {
363  const VArray<float3> &vector = get_vector(0);
364  if (compute_factor) {
365  for (int64_t i : mask) {
366  const float3 pxyz = vector[i] * scale[i];
367  const float2 position = float2(pxyz[0], pxyz[1]);
369  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
370  }
371  }
372  break;
373  }
374  case 3: {
375  const VArray<float3> &vector = get_vector(0);
376  if (compute_factor) {
377  for (int64_t i : mask) {
378  const float3 position = vector[i] * scale[i];
380  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
381  }
382  }
383  break;
384  }
385  case 4: {
386  const VArray<float3> &vector = get_vector(0);
387  const VArray<float> &w = get_w(1);
388  if (compute_factor) {
389  for (int64_t i : mask) {
390  const float3 pxyz = vector[i] * scale[i];
391  const float pw = w[i] * scale[i];
392  const float4 position{pxyz[0], pxyz[1], pxyz[2], pw};
394  position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]);
395  }
396  }
397  break;
398  }
399  }
400  break;
401  }
402  case SHD_MUSGRAVE_FBM: {
403  MutableSpan<float> r_factor = get_r_factor(param++);
404  const bool compute_factor = !r_factor.is_empty();
405  switch (dimensions_) {
406  case 1: {
407  const VArray<float> &w = get_w(0);
408  if (compute_factor) {
409  for (int64_t i : mask) {
410  const float position = w[i] * scale[i];
411  r_factor[i] = noise::musgrave_fBm(
412  position, dimension[i], lacunarity[i], detail[i]);
413  }
414  }
415  break;
416  }
417  case 2: {
418  const VArray<float3> &vector = get_vector(0);
419  if (compute_factor) {
420  for (int64_t i : mask) {
421  const float3 pxyz = vector[i] * scale[i];
422  const float2 position = float2(pxyz[0], pxyz[1]);
423  r_factor[i] = noise::musgrave_fBm(
424  position, dimension[i], lacunarity[i], detail[i]);
425  }
426  }
427  break;
428  }
429  case 3: {
430  const VArray<float3> &vector = get_vector(0);
431  if (compute_factor) {
432  for (int64_t i : mask) {
433  const float3 position = vector[i] * scale[i];
434  r_factor[i] = noise::musgrave_fBm(
435  position, dimension[i], lacunarity[i], detail[i]);
436  }
437  }
438  break;
439  }
440  case 4: {
441  const VArray<float3> &vector = get_vector(0);
442  const VArray<float> &w = get_w(1);
443  if (compute_factor) {
444  for (int64_t i : mask) {
445  const float3 pxyz = vector[i] * scale[i];
446  const float pw = w[i] * scale[i];
447  const float4 position{pxyz[0], pxyz[1], pxyz[2], pw};
448  r_factor[i] = noise::musgrave_fBm(
449  position, dimension[i], lacunarity[i], detail[i]);
450  }
451  }
452  break;
453  }
454  }
455  break;
456  }
458  const VArray<float> &offset = get_offset(param++);
459  MutableSpan<float> r_factor = get_r_factor(param++);
460  const bool compute_factor = !r_factor.is_empty();
461  switch (dimensions_) {
462  case 1: {
463  const VArray<float> &w = get_w(0);
464  if (compute_factor) {
465  for (int64_t i : mask) {
466  const float position = w[i] * scale[i];
467  r_factor[i] = noise::musgrave_hetero_terrain(
468  position, dimension[i], lacunarity[i], detail[i], offset[i]);
469  }
470  }
471  break;
472  }
473  case 2: {
474  const VArray<float3> &vector = get_vector(0);
475  if (compute_factor) {
476  for (int64_t i : mask) {
477  const float3 pxyz = vector[i] * scale[i];
478  const float2 position = float2(pxyz[0], pxyz[1]);
479  r_factor[i] = noise::musgrave_hetero_terrain(
480  position, dimension[i], lacunarity[i], detail[i], offset[i]);
481  }
482  }
483  break;
484  }
485  case 3: {
486  const VArray<float3> &vector = get_vector(0);
487  if (compute_factor) {
488  for (int64_t i : mask) {
489  const float3 position = vector[i] * scale[i];
490  r_factor[i] = noise::musgrave_hetero_terrain(
491  position, dimension[i], lacunarity[i], detail[i], offset[i]);
492  }
493  }
494  break;
495  }
496  case 4: {
497  const VArray<float3> &vector = get_vector(0);
498  const VArray<float> &w = get_w(1);
499  if (compute_factor) {
500  for (int64_t i : mask) {
501  const float3 pxyz = vector[i] * scale[i];
502  const float pw = w[i] * scale[i];
503  const float4 position{pxyz[0], pxyz[1], pxyz[2], pw};
504  r_factor[i] = noise::musgrave_hetero_terrain(
505  position, dimension[i], lacunarity[i], detail[i], offset[i]);
506  }
507  }
508  break;
509  }
510  }
511  break;
512  }
513  }
514  }
515 };
516 
518 {
519  bNode &node = builder.node();
520  NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage;
521  builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type);
522 }
523 
524 } // namespace blender::nodes::node_shader_tex_musgrave_cc
525 
527 {
529 
530  static bNodeType ntype;
531 
532  sh_fn_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE);
538  &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage);
542 
543  nodeRegisterType(&ntype);
544 }
void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpu_fn)
Definition: node.cc:4465
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
void node_type_size_preset(struct bNodeType *ntype, eNodeSizePreset size)
Definition: node.cc:4408
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
struct bNodeSocket * nodeFindSocket(const struct bNode *node, eNodeSocketInOut in_out, const char *identifier)
#define NODE_CLASS_TEXTURE
Definition: BKE_node.h:355
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
@ NODE_SIZE_MIDDLE
Definition: BKE_node.h:366
#define SH_NODE_TEX_MUSGRAVE
Definition: BKE_node.h:1125
void BKE_texture_mapping_default(struct TexMapping *texmap, int type)
Definition: texture.c:238
void BKE_texture_colormapping_default(struct ColorMapping *colormap)
Definition: texture.c:340
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define UNUSED(x)
#define ELEM(...)
#define SHD_MUSGRAVE_MULTIFRACTAL
#define SHD_MUSGRAVE_RIDGED_MULTIFRACTAL
@ SOCK_OUT
@ SOCK_IN
#define SHD_MUSGRAVE_FBM
#define SHD_MUSGRAVE_HYBRID_MULTIFRACTAL
#define SHD_MUSGRAVE_HETERO_TERRAIN
#define TEXMAP_TYPE_POINT
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
bool GPU_stack_link(GPUMaterial *mat, struct bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
constexpr bool is_empty() const
Definition: BLI_span.hh:519
void set_signature(const MFSignature *signature)
const MFSignature & signature() const
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
MusgraveFunction(const int dimensions, const int musgrave_type)
static fn::MFSignature create_signature(const int dimensions, const int musgrave_type)
OperationNode * node
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static const char * gpu_shader_name_get(const int type, const int dimensions)
static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder)
static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b)
static void node_shader_update_tex_musgrave(bNodeTree *ntree, bNode *node)
static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node)
float musgrave_multi_fractal(float co, float H, float lacunarity, float octaves)
Definition: noise.cc:747
float musgrave_ridged_multi_fractal(float co, float H, float lacunarity, float octaves, float offset, float gain)
Definition: noise.cc:843
float musgrave_fBm(float co, float H, float lacunarity, float octaves)
Definition: noise.cc:720
float musgrave_hybrid_multi_fractal(float co, float H, float lacunarity, float octaves, float offset, float gain)
Definition: noise.cc:803
float musgrave_hetero_terrain(float co, float H, float lacunarity, float octaves, float offset)
Definition: noise.cc:772
vec_base< float, 2 > float2
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void register_node_type_sh_tex_musgrave()
void node_shader_gpu_tex_mapping(GPUMaterial *mat, bNode *node, GPUNodeStack *in, GPUNodeStack *UNUSED(out))
void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void node_sock_label(bNodeSocket *sock, const char *name)
Definition: node_util.c:76
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
#define min(a, b)
Definition: sort.c:35
__int64 int64_t
Definition: stdint.h:89
Defines a node type.
Definition: BKE_node.h:226
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeMultiFunctionBuildFunction build_multi_function
Definition: BKE_node.h:313
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480