Blender  V2.93
mesh_displace.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "device/device.h"
18 
19 #include "render/mesh.h"
20 #include "render/object.h"
21 #include "render/scene.h"
22 #include "render/shader.h"
23 
24 #include "util/util_foreach.h"
25 #include "util/util_map.h"
26 #include "util/util_progress.h"
27 #include "util/util_set.h"
28 
30 
32 {
33  float3 v0 = verts[t.v[0]];
34  float3 v1 = verts[t.v[1]];
35  float3 v2 = verts[t.v[2]];
36 
37  float3 norm = cross(v1 - v0, v2 - v0);
38  float normlen = len(norm);
39 
40  if (normlen == 0.0f)
41  return make_float3(1.0f, 0.0f, 0.0f);
42 
43  return norm / normlen;
44 }
45 
47  Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress)
48 {
49  /* verify if we have a displacement shader */
50  if (!mesh->has_true_displacement()) {
51  return false;
52  }
53 
54  string msg = string_printf("Computing Displacement %s", mesh->name.c_str());
55  progress.set_status("Updating Mesh", msg);
56 
57  /* find object index. todo: is arbitrary */
58  size_t object_index = OBJECT_NONE;
59 
60  for (size_t i = 0; i < scene->objects.size(); i++) {
61  if (scene->objects[i]->get_geometry() == mesh) {
62  object_index = i;
63  break;
64  }
65  }
66 
67  /* setup input for device task */
68  const size_t num_verts = mesh->verts.size();
69  vector<bool> done(num_verts, false);
70  device_vector<uint4> d_input(device, "displace_input", MEM_READ_ONLY);
71  uint4 *d_input_data = d_input.alloc(num_verts);
72  size_t d_input_size = 0;
73 
74  size_t num_triangles = mesh->num_triangles();
75  for (size_t i = 0; i < num_triangles; i++) {
77  int shader_index = mesh->shader[i];
78  Shader *shader = (shader_index < mesh->used_shaders.size()) ?
79  static_cast<Shader *>(mesh->used_shaders[shader_index]) :
81 
82  if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) {
83  continue;
84  }
85 
86  for (int j = 0; j < 3; j++) {
87  if (done[t.v[j]])
88  continue;
89 
90  done[t.v[j]] = true;
91 
92  /* set up object, primitive and barycentric coordinates */
93  int object = object_index;
94  int prim = mesh->prim_offset + i;
95  float u, v;
96 
97  switch (j) {
98  case 0:
99  u = 1.0f;
100  v = 0.0f;
101  break;
102  case 1:
103  u = 0.0f;
104  v = 1.0f;
105  break;
106  default:
107  u = 0.0f;
108  v = 0.0f;
109  break;
110  }
111 
112  /* back */
113  uint4 in = make_uint4(object, prim, __float_as_int(u), __float_as_int(v));
114  d_input_data[d_input_size++] = in;
115  }
116  }
117 
118  if (d_input_size == 0)
119  return false;
120 
121  /* run device task */
122  device_vector<float4> d_output(device, "displace_output", MEM_READ_WRITE);
123  d_output.alloc(d_input_size);
124  d_output.zero_to_device();
125  d_input.copy_to_device();
126 
127  /* needs to be up to data for attribute access */
128  device->const_copy_to("__data", &dscene->data, sizeof(dscene->data));
129 
131  task.shader_input = d_input.device_pointer;
132  task.shader_output = d_output.device_pointer;
133  task.shader_eval_type = SHADER_EVAL_DISPLACE;
134  task.shader_x = 0;
135  task.shader_w = d_output.size();
136  task.num_samples = 1;
137  task.get_cancel = function_bind(&Progress::get_cancel, &progress);
138 
139  device->task_add(task);
140  device->task_wait();
141 
142  if (progress.get_cancel()) {
143  d_input.free();
144  d_output.free();
145  return false;
146  }
147 
148  d_output.copy_from_device(0, 1, d_output.size());
149  d_input.free();
150 
151  /* read result */
152  done.clear();
153  done.resize(num_verts, false);
154  int k = 0;
155 
156  float4 *offset = d_output.data();
157 
159  for (size_t i = 0; i < num_triangles; i++) {
161  int shader_index = mesh->shader[i];
162  Shader *shader = (shader_index < mesh->used_shaders.size()) ?
163  static_cast<Shader *>(mesh->used_shaders[shader_index]) :
165 
166  if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) {
167  continue;
168  }
169 
170  for (int j = 0; j < 3; j++) {
171  if (!done[t.v[j]]) {
172  done[t.v[j]] = true;
173  float3 off = float4_to_float3(offset[k++]);
174  /* Avoid illegal vertex coordinates. */
175  off = ensure_finite3(off);
176  mesh->verts[t.v[j]] += off;
177  if (attr_mP != NULL) {
178  for (int step = 0; step < mesh->motion_steps - 1; step++) {
179  float3 *mP = attr_mP->data_float3() + step * num_verts;
180  mP[t.v[j]] += off;
181  }
182  }
183  }
184  }
185  }
186 
187  d_output.free();
188 
189  /* stitch */
190  unordered_set<int> stitch_keys;
191  for (pair<int, int> i : mesh->vert_to_stitching_key_map) {
192  stitch_keys.insert(i.second); /* stitching index */
193  }
194 
195  typedef unordered_multimap<int, int>::iterator map_it_t;
196 
197  for (int key : stitch_keys) {
198  pair<map_it_t, map_it_t> verts = mesh->vert_stitching_map.equal_range(key);
199 
200  float3 pos = zero_float3();
201  int num = 0;
202 
203  for (map_it_t v = verts.first; v != verts.second; ++v) {
204  int vert = v->second;
205 
206  pos += mesh->verts[vert];
207  num++;
208  }
209 
210  if (num <= 1) {
211  continue;
212  }
213 
214  pos *= 1.0f / num;
215 
216  for (map_it_t v = verts.first; v != verts.second; ++v) {
217  mesh->verts[v->second] = pos;
218  }
219  }
220 
221  /* for displacement method both, we only need to recompute the face
222  * normals, as bump mapping in the shader will already alter the
223  * vertex normal, so we start from the non-displaced vertex normals
224  * to avoid applying the perturbation twice. */
227 
228  bool need_recompute_vertex_normals = false;
229 
230  foreach (Node *node, mesh->get_used_shaders()) {
231  Shader *shader = static_cast<Shader *>(node);
232  if (shader->has_displacement && shader->get_displacement_method() == DISPLACE_TRUE) {
233  need_recompute_vertex_normals = true;
234  break;
235  }
236  }
237 
238  if (need_recompute_vertex_normals) {
239  bool flip = mesh->transform_negative_scaled;
240  vector<bool> tri_has_true_disp(num_triangles, false);
241 
242  for (size_t i = 0; i < num_triangles; i++) {
243  int shader_index = mesh->shader[i];
244  Shader *shader = (shader_index < mesh->used_shaders.size()) ?
245  static_cast<Shader *>(mesh->used_shaders[shader_index]) :
247 
248  tri_has_true_disp[i] = shader->has_displacement &&
249  shader->get_displacement_method() == DISPLACE_TRUE;
250  }
251 
252  /* static vertex normals */
253 
254  /* get attributes */
257 
258  float3 *fN = attr_fN->data_float3();
259  float3 *vN = attr_vN->data_float3();
260 
261  /* compute vertex normals */
262 
263  /* zero vertex normals on triangles with true displacement */
264  for (size_t i = 0; i < num_triangles; i++) {
265  if (tri_has_true_disp[i]) {
266  for (size_t j = 0; j < 3; j++) {
267  vN[mesh->get_triangle(i).v[j]] = zero_float3();
268  }
269  }
270  }
271 
272  /* add face normals to vertex normals */
273  for (size_t i = 0; i < num_triangles; i++) {
274  if (tri_has_true_disp[i]) {
275  for (size_t j = 0; j < 3; j++) {
276  int vert = mesh->get_triangle(i).v[j];
277  vN[vert] += fN[i];
278 
279  /* add face normals to stitched vertices */
280  if (stitch_keys.size()) {
281  map_it_t key = mesh->vert_to_stitching_key_map.find(vert);
282 
283  if (key != mesh->vert_to_stitching_key_map.end()) {
284  pair<map_it_t, map_it_t> verts = mesh->vert_stitching_map.equal_range(key->second);
285 
286  for (map_it_t v = verts.first; v != verts.second; ++v) {
287  if (v->second == vert) {
288  continue;
289  }
290 
291  vN[v->second] += fN[i];
292  }
293  }
294  }
295  }
296  }
297  }
298 
299  /* normalize vertex normals */
300  done.clear();
301  done.resize(num_verts, false);
302 
303  for (size_t i = 0; i < num_triangles; i++) {
304  if (tri_has_true_disp[i]) {
305  for (size_t j = 0; j < 3; j++) {
306  int vert = mesh->get_triangle(i).v[j];
307 
308  if (done[vert]) {
309  continue;
310  }
311 
312  vN[vert] = normalize(vN[vert]);
313  if (flip)
314  vN[vert] = -vN[vert];
315 
316  done[vert] = true;
317  }
318  }
319  }
320 
321  /* motion vertex normals */
324 
325  if (mesh->has_motion_blur() && attr_mP && attr_mN) {
326  for (int step = 0; step < mesh->motion_steps - 1; step++) {
327  float3 *mP = attr_mP->data_float3() + step * mesh->verts.size();
328  float3 *mN = attr_mN->data_float3() + step * mesh->verts.size();
329 
330  /* compute */
331 
332  /* zero vertex normals on triangles with true displacement */
333  for (size_t i = 0; i < num_triangles; i++) {
334  if (tri_has_true_disp[i]) {
335  for (size_t j = 0; j < 3; j++) {
336  mN[mesh->get_triangle(i).v[j]] = zero_float3();
337  }
338  }
339  }
340 
341  /* add face normals to vertex normals */
342  for (size_t i = 0; i < num_triangles; i++) {
343  if (tri_has_true_disp[i]) {
344  for (size_t j = 0; j < 3; j++) {
345  int vert = mesh->get_triangle(i).v[j];
347  mN[vert] += fN;
348 
349  /* add face normals to stitched vertices */
350  if (stitch_keys.size()) {
351  map_it_t key = mesh->vert_to_stitching_key_map.find(vert);
352 
353  if (key != mesh->vert_to_stitching_key_map.end()) {
354  pair<map_it_t, map_it_t> verts = mesh->vert_stitching_map.equal_range(
355  key->second);
356 
357  for (map_it_t v = verts.first; v != verts.second; ++v) {
358  if (v->second == vert) {
359  continue;
360  }
361 
362  mN[v->second] += fN;
363  }
364  }
365  }
366  }
367  }
368  }
369 
370  /* normalize vertex normals */
371  done.clear();
372  done.resize(num_verts, false);
373 
374  for (size_t i = 0; i < num_triangles; i++) {
375  if (tri_has_true_disp[i]) {
376  for (size_t j = 0; j < 3; j++) {
377  int vert = mesh->get_triangle(i).v[j];
378 
379  if (done[vert]) {
380  continue;
381  }
382 
383  mN[vert] = normalize(mN[vert]);
384  if (flip)
385  mN[vert] = -mN[vert];
386 
387  done[vert] = true;
388  }
389  }
390  }
391  }
392  }
393  }
394 
395  return true;
396 }
397 
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
_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 const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition: btVector3.h:263
Attribute * find(ustring name) const
Definition: attribute.cpp:447
void remove(ustring name)
Definition: attribute.cpp:456
float3 * data_float3()
Definition: attribute.h:86
KernelData data
Definition: scene.h:136
Definition: device.h:293
virtual void const_copy_to(const char *name, void *host, size_t size)=0
virtual void task_wait()=0
virtual void task_add(DeviceTask &task)=0
bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress)
bool has_true_displacement() const
Definition: geometry.cpp:187
size_t prim_offset
Definition: geometry.h:102
bool has_motion_blur() const
Definition: geometry.cpp:255
AttributeSet attributes
Definition: geometry.h:81
bool transform_negative_scaled
Definition: geometry.h:89
bool get_cancel()
void set_status(const string &status_, const string &substatus_="")
Definition: shader.h:80
device_ptr device_pointer
T * alloc(size_t width, size_t height=0, size_t depth=0)
void copy_from_device()
size_t size() const
void copy_to_device()
void zero_to_device()
OperationNode * node
Scene scene
#define function_bind
@ MEM_READ_WRITE
Definition: device_memory.h:37
@ MEM_READ_ONLY
Definition: device_memory.h:36
static float verts[][3]
uint pos
#define CCL_NAMESPACE_END
#define make_float3(x, y, z)
void KERNEL_FUNCTION_FULL_NAME() shader(KernelGlobals *kg, uint4 *input, float4 *output, int type, int filter, int i, int offset, int sample)
@ ATTR_STD_MOTION_VERTEX_NORMAL
Definition: kernel_types.h:757
@ ATTR_STD_VERTEX_NORMAL
Definition: kernel_types.h:746
@ ATTR_STD_MOTION_VERTEX_POSITION
Definition: kernel_types.h:756
@ ATTR_STD_FACE_NORMAL
Definition: kernel_types.h:747
#define OBJECT_NONE
Definition: kernel_types.h:59
@ SHADER_EVAL_DISPLACE
Definition: kernel_types.h:197
static CCL_NAMESPACE_BEGIN float3 compute_face_normal(const Mesh::Triangle &t, float3 *verts)
struct blender::compositor::@172::@174 task
@ DISPLACE_BUMP
Definition: shader.h:67
@ DISPLACE_TRUE
Definition: shader.h:68
int v[3]
Definition: mesh.h:63
float size[3]
Triangle get_triangle(size_t i) const
Definition: mesh.h:86
size_t num_triangles() const
Definition: mesh.h:92
void add_face_normals()
Definition: mesh.cpp:535
Definition: node.h:98
ustring name
Definition: node.h:174
Shader * default_surface
Definition: scene.h:253
vector< Object * > objects
Definition: scene.h:234
__forceinline avxf cross(const avxf &a, const avxf &b)
Definition: util_avxf.h:119
ccl_device_inline int __float_as_int(float f)
Definition: util_math.h:202
ccl_device_inline float3 float4_to_float3(const float4 a)
Definition: util_math.h:415
ccl_device_inline float2 normalize(const float2 &a)
ccl_device_inline float3 zero_float3()
ccl_device_inline float3 ensure_finite3(float3 v)
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition: util_string.cpp:32
ccl_device_inline uint4 make_uint4(uint x, uint y, uint z, uint w)
uint len