ClangFormat: format '#if 0' code in intern/
[blender.git] / intern / cycles / kernel / osl / osl_services.cpp
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 /* TODO(sergey): There is a bit of headers dependency hell going on
18  * here, so for now we just put here. In the future it might be better
19  * to have dedicated file for such tweaks.
20  */
21 #if (defined(__GNUC__) && !defined(__clang__)) && defined(NDEBUG)
22 #  pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
23 #  pragma GCC diagnostic ignored "-Wuninitialized"
24 #endif
25
26 #include <string.h>
27
28 #include "render/mesh.h"
29 #include "render/object.h"
30 #include "render/scene.h"
31
32 #include "kernel/osl/osl_closures.h"
33 #include "kernel/osl/osl_globals.h"
34 #include "kernel/osl/osl_services.h"
35 #include "kernel/osl/osl_shader.h"
36
37 #include "util/util_foreach.h"
38 #include "util/util_logging.h"
39 #include "util/util_string.h"
40
41 #include "kernel/kernel_compat_cpu.h"
42 #include "kernel/split/kernel_split_data_types.h"
43 #include "kernel/kernel_globals.h"
44 #include "kernel/kernel_color.h"
45 #include "kernel/kernel_random.h"
46 #include "kernel/kernel_projection.h"
47 #include "kernel/kernel_differential.h"
48 #include "kernel/kernel_montecarlo.h"
49 #include "kernel/kernel_camera.h"
50 #include "kernel/kernels/cpu/kernel_cpu_image.h"
51 #include "kernel/geom/geom.h"
52 #include "kernel/bvh/bvh.h"
53
54 #include "kernel/kernel_projection.h"
55 #include "kernel/kernel_accumulate.h"
56 #include "kernel/kernel_shader.h"
57
58 #ifdef WITH_PTEX
59 #  include <Ptexture.h>
60 #endif
61
62 CCL_NAMESPACE_BEGIN
63
64 /* RenderServices implementation */
65
66 static void copy_matrix(OSL::Matrix44 &m, const Transform &tfm)
67 {
68   ProjectionTransform t = projection_transpose(ProjectionTransform(tfm));
69   memcpy((void *)&m, &t, sizeof(m));
70 }
71
72 static void copy_matrix(OSL::Matrix44 &m, const ProjectionTransform &tfm)
73 {
74   ProjectionTransform t = projection_transpose(tfm);
75   memcpy((void *)&m, &t, sizeof(m));
76 }
77
78 /* static ustrings */
79 ustring OSLRenderServices::u_distance("distance");
80 ustring OSLRenderServices::u_index("index");
81 ustring OSLRenderServices::u_world("world");
82 ustring OSLRenderServices::u_camera("camera");
83 ustring OSLRenderServices::u_screen("screen");
84 ustring OSLRenderServices::u_raster("raster");
85 ustring OSLRenderServices::u_ndc("NDC");
86 ustring OSLRenderServices::u_object_location("object:location");
87 ustring OSLRenderServices::u_object_index("object:index");
88 ustring OSLRenderServices::u_geom_dupli_generated("geom:dupli_generated");
89 ustring OSLRenderServices::u_geom_dupli_uv("geom:dupli_uv");
90 ustring OSLRenderServices::u_material_index("material:index");
91 ustring OSLRenderServices::u_object_random("object:random");
92 ustring OSLRenderServices::u_particle_index("particle:index");
93 ustring OSLRenderServices::u_particle_random("particle:random");
94 ustring OSLRenderServices::u_particle_age("particle:age");
95 ustring OSLRenderServices::u_particle_lifetime("particle:lifetime");
96 ustring OSLRenderServices::u_particle_location("particle:location");
97 ustring OSLRenderServices::u_particle_rotation("particle:rotation");
98 ustring OSLRenderServices::u_particle_size("particle:size");
99 ustring OSLRenderServices::u_particle_velocity("particle:velocity");
100 ustring OSLRenderServices::u_particle_angular_velocity("particle:angular_velocity");
101 ustring OSLRenderServices::u_geom_numpolyvertices("geom:numpolyvertices");
102 ustring OSLRenderServices::u_geom_trianglevertices("geom:trianglevertices");
103 ustring OSLRenderServices::u_geom_polyvertices("geom:polyvertices");
104 ustring OSLRenderServices::u_geom_name("geom:name");
105 ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
106 ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
107 ustring OSLRenderServices::u_is_curve("geom:is_curve");
108 ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
109 ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
110 ustring OSLRenderServices::u_curve_random("geom:curve_random");
111 ustring OSLRenderServices::u_path_ray_length("path:ray_length");
112 ustring OSLRenderServices::u_path_ray_depth("path:ray_depth");
113 ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth");
114 ustring OSLRenderServices::u_path_glossy_depth("path:glossy_depth");
115 ustring OSLRenderServices::u_path_transparent_depth("path:transparent_depth");
116 ustring OSLRenderServices::u_path_transmission_depth("path:transmission_depth");
117 ustring OSLRenderServices::u_trace("trace");
118 ustring OSLRenderServices::u_hit("hit");
119 ustring OSLRenderServices::u_hitdist("hitdist");
120 ustring OSLRenderServices::u_N("N");
121 ustring OSLRenderServices::u_Ng("Ng");
122 ustring OSLRenderServices::u_P("P");
123 ustring OSLRenderServices::u_I("I");
124 ustring OSLRenderServices::u_u("u");
125 ustring OSLRenderServices::u_v("v");
126 ustring OSLRenderServices::u_empty;
127 ustring OSLRenderServices::u_at_bevel("@bevel");
128 ustring OSLRenderServices::u_at_ao("@ao");
129
130 OSLRenderServices::OSLRenderServices()
131 {
132   kernel_globals = NULL;
133   osl_ts = NULL;
134
135 #ifdef WITH_PTEX
136   size_t maxmem = 16384 * 1024;
137   ptex_cache = PtexCache::create(0, maxmem);
138 #endif
139 }
140
141 OSLRenderServices::~OSLRenderServices()
142 {
143   if (osl_ts) {
144     VLOG(2) << "OSL texture system stats:\n" << osl_ts->getstats();
145   }
146 #ifdef WITH_PTEX
147   ptex_cache->release();
148 #endif
149 }
150
151 void OSLRenderServices::thread_init(KernelGlobals *kernel_globals_, OSL::TextureSystem *osl_ts_)
152 {
153   kernel_globals = kernel_globals_;
154   osl_ts = osl_ts_;
155 }
156
157 bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
158                                    OSL::Matrix44 &result,
159                                    OSL::TransformationPtr xform,
160                                    float time)
161 {
162   /* this is only used for shader and object space, we don't really have
163    * a concept of shader space, so we just use object space for both. */
164   if (xform) {
165     const ShaderData *sd = (const ShaderData *)xform;
166     KernelGlobals *kg = sd->osl_globals;
167     int object = sd->object;
168
169     if (object != OBJECT_NONE) {
170 #ifdef __OBJECT_MOTION__
171       Transform tfm;
172
173       if (time == sd->time)
174         tfm = sd->ob_tfm;
175       else
176         tfm = object_fetch_transform_motion_test(kg, object, time, NULL);
177 #else
178       Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
179 #endif
180       copy_matrix(result, tfm);
181
182       return true;
183     }
184     else if (sd->type == PRIMITIVE_LAMP) {
185       copy_matrix(result, sd->ob_tfm);
186
187       return true;
188     }
189   }
190
191   return false;
192 }
193
194 bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
195                                            OSL::Matrix44 &result,
196                                            OSL::TransformationPtr xform,
197                                            float time)
198 {
199   /* this is only used for shader and object space, we don't really have
200    * a concept of shader space, so we just use object space for both. */
201   if (xform) {
202     const ShaderData *sd = (const ShaderData *)xform;
203     KernelGlobals *kg = sd->osl_globals;
204     int object = sd->object;
205
206     if (object != OBJECT_NONE) {
207 #ifdef __OBJECT_MOTION__
208       Transform itfm;
209
210       if (time == sd->time)
211         itfm = sd->ob_itfm;
212       else
213         object_fetch_transform_motion_test(kg, object, time, &itfm);
214 #else
215       Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
216 #endif
217       copy_matrix(result, itfm);
218
219       return true;
220     }
221     else if (sd->type == PRIMITIVE_LAMP) {
222       copy_matrix(result, sd->ob_itfm);
223
224       return true;
225     }
226   }
227
228   return false;
229 }
230
231 bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
232                                    OSL::Matrix44 &result,
233                                    ustring from,
234                                    float time)
235 {
236   KernelGlobals *kg = kernel_globals;
237
238   if (from == u_ndc) {
239     copy_matrix(result, kernel_data.cam.ndctoworld);
240     return true;
241   }
242   else if (from == u_raster) {
243     copy_matrix(result, kernel_data.cam.rastertoworld);
244     return true;
245   }
246   else if (from == u_screen) {
247     copy_matrix(result, kernel_data.cam.screentoworld);
248     return true;
249   }
250   else if (from == u_camera) {
251     copy_matrix(result, kernel_data.cam.cameratoworld);
252     return true;
253   }
254   else if (from == u_world) {
255     result.makeIdentity();
256     return true;
257   }
258
259   return false;
260 }
261
262 bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
263                                            OSL::Matrix44 &result,
264                                            ustring to,
265                                            float time)
266 {
267   KernelGlobals *kg = kernel_globals;
268
269   if (to == u_ndc) {
270     copy_matrix(result, kernel_data.cam.worldtondc);
271     return true;
272   }
273   else if (to == u_raster) {
274     copy_matrix(result, kernel_data.cam.worldtoraster);
275     return true;
276   }
277   else if (to == u_screen) {
278     copy_matrix(result, kernel_data.cam.worldtoscreen);
279     return true;
280   }
281   else if (to == u_camera) {
282     copy_matrix(result, kernel_data.cam.worldtocamera);
283     return true;
284   }
285   else if (to == u_world) {
286     result.makeIdentity();
287     return true;
288   }
289
290   return false;
291 }
292
293 bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
294                                    OSL::Matrix44 &result,
295                                    OSL::TransformationPtr xform)
296 {
297   /* this is only used for shader and object space, we don't really have
298    * a concept of shader space, so we just use object space for both. */
299   if (xform) {
300     const ShaderData *sd = (const ShaderData *)xform;
301     int object = sd->object;
302
303     if (object != OBJECT_NONE) {
304 #ifdef __OBJECT_MOTION__
305       Transform tfm = sd->ob_tfm;
306 #else
307       KernelGlobals *kg = sd->osl_globals;
308       Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
309 #endif
310       copy_matrix(result, tfm);
311
312       return true;
313     }
314     else if (sd->type == PRIMITIVE_LAMP) {
315       copy_matrix(result, sd->ob_tfm);
316
317       return true;
318     }
319   }
320
321   return false;
322 }
323
324 bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
325                                            OSL::Matrix44 &result,
326                                            OSL::TransformationPtr xform)
327 {
328   /* this is only used for shader and object space, we don't really have
329    * a concept of shader space, so we just use object space for both. */
330   if (xform) {
331     const ShaderData *sd = (const ShaderData *)xform;
332     int object = sd->object;
333
334     if (object != OBJECT_NONE) {
335 #ifdef __OBJECT_MOTION__
336       Transform tfm = sd->ob_itfm;
337 #else
338       KernelGlobals *kg = sd->osl_globals;
339       Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
340 #endif
341       copy_matrix(result, tfm);
342
343       return true;
344     }
345     else if (sd->type == PRIMITIVE_LAMP) {
346       copy_matrix(result, sd->ob_itfm);
347
348       return true;
349     }
350   }
351
352   return false;
353 }
354
355 bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring from)
356 {
357   KernelGlobals *kg = kernel_globals;
358
359   if (from == u_ndc) {
360     copy_matrix(result, kernel_data.cam.ndctoworld);
361     return true;
362   }
363   else if (from == u_raster) {
364     copy_matrix(result, kernel_data.cam.rastertoworld);
365     return true;
366   }
367   else if (from == u_screen) {
368     copy_matrix(result, kernel_data.cam.screentoworld);
369     return true;
370   }
371   else if (from == u_camera) {
372     copy_matrix(result, kernel_data.cam.cameratoworld);
373     return true;
374   }
375
376   return false;
377 }
378
379 bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
380                                            OSL::Matrix44 &result,
381                                            ustring to)
382 {
383   KernelGlobals *kg = kernel_globals;
384
385   if (to == u_ndc) {
386     copy_matrix(result, kernel_data.cam.worldtondc);
387     return true;
388   }
389   else if (to == u_raster) {
390     copy_matrix(result, kernel_data.cam.worldtoraster);
391     return true;
392   }
393   else if (to == u_screen) {
394     copy_matrix(result, kernel_data.cam.worldtoscreen);
395     return true;
396   }
397   else if (to == u_camera) {
398     copy_matrix(result, kernel_data.cam.worldtocamera);
399     return true;
400   }
401
402   return false;
403 }
404
405 bool OSLRenderServices::get_array_attribute(OSL::ShaderGlobals *sg,
406                                             bool derivatives,
407                                             ustring object,
408                                             TypeDesc type,
409                                             ustring name,
410                                             int index,
411                                             void *val)
412 {
413   return false;
414 }
415
416 static bool set_attribute_float2(float2 f[3], TypeDesc type, bool derivatives, void *val)
417 {
418   if (type == TypeDesc::TypePoint || type == TypeDesc::TypeVector ||
419       type == TypeDesc::TypeNormal || type == TypeDesc::TypeColor) {
420     float *fval = (float *)val;
421
422     fval[0] = f[0].x;
423     fval[1] = f[0].y;
424     fval[2] = 0.0f;
425
426     if (derivatives) {
427       fval[3] = f[1].x;
428       fval[4] = f[1].y;
429       fval[5] = 0.0f;
430
431       fval[6] = f[2].x;
432       fval[7] = f[2].y;
433       fval[8] = 0.0f;
434     }
435
436     return true;
437   }
438   else if (type == TypeDesc::TypeFloat) {
439     float *fval = (float *)val;
440     fval[0] = average(f[0]);
441
442     if (derivatives) {
443       fval[1] = average(f[1]);
444       fval[2] = average(f[2]);
445     }
446
447     return true;
448   }
449
450   return false;
451 }
452
453 static bool set_attribute_float3(float3 f[3], TypeDesc type, bool derivatives, void *val)
454 {
455   if (type == TypeDesc::TypePoint || type == TypeDesc::TypeVector ||
456       type == TypeDesc::TypeNormal || type == TypeDesc::TypeColor) {
457     float *fval = (float *)val;
458
459     fval[0] = f[0].x;
460     fval[1] = f[0].y;
461     fval[2] = f[0].z;
462
463     if (derivatives) {
464       fval[3] = f[1].x;
465       fval[4] = f[1].y;
466       fval[5] = f[1].z;
467
468       fval[6] = f[2].x;
469       fval[7] = f[2].y;
470       fval[8] = f[2].z;
471     }
472
473     return true;
474   }
475   else if (type == TypeDesc::TypeFloat) {
476     float *fval = (float *)val;
477     fval[0] = average(f[0]);
478
479     if (derivatives) {
480       fval[1] = average(f[1]);
481       fval[2] = average(f[2]);
482     }
483
484     return true;
485   }
486
487   return false;
488 }
489
490 static bool set_attribute_float3(float3 f, TypeDesc type, bool derivatives, void *val)
491 {
492   float3 fv[3];
493
494   fv[0] = f;
495   fv[1] = make_float3(0.0f, 0.0f, 0.0f);
496   fv[2] = make_float3(0.0f, 0.0f, 0.0f);
497
498   return set_attribute_float3(fv, type, derivatives, val);
499 }
500
501 static bool set_attribute_float(float f[3], TypeDesc type, bool derivatives, void *val)
502 {
503   if (type == TypeDesc::TypePoint || type == TypeDesc::TypeVector ||
504       type == TypeDesc::TypeNormal || type == TypeDesc::TypeColor) {
505     float *fval = (float *)val;
506     fval[0] = f[0];
507     fval[1] = f[1];
508     fval[2] = f[2];
509
510     if (derivatives) {
511       fval[3] = f[1];
512       fval[4] = f[1];
513       fval[5] = f[1];
514
515       fval[6] = f[2];
516       fval[7] = f[2];
517       fval[8] = f[2];
518     }
519
520     return true;
521   }
522   else if (type == TypeDesc::TypeFloat) {
523     float *fval = (float *)val;
524     fval[0] = f[0];
525
526     if (derivatives) {
527       fval[1] = f[1];
528       fval[2] = f[2];
529     }
530
531     return true;
532   }
533
534   return false;
535 }
536
537 static bool set_attribute_float(float f, TypeDesc type, bool derivatives, void *val)
538 {
539   float fv[3];
540
541   fv[0] = f;
542   fv[1] = 0.0f;
543   fv[2] = 0.0f;
544
545   return set_attribute_float(fv, type, derivatives, val);
546 }
547
548 static bool set_attribute_int(int i, TypeDesc type, bool derivatives, void *val)
549 {
550   if (type.basetype == TypeDesc::INT && type.aggregate == TypeDesc::SCALAR && type.arraylen == 0) {
551     int *ival = (int *)val;
552     ival[0] = i;
553
554     if (derivatives) {
555       ival[1] = 0;
556       ival[2] = 0;
557     }
558
559     return true;
560   }
561
562   return false;
563 }
564
565 static bool set_attribute_string(ustring str, TypeDesc type, bool derivatives, void *val)
566 {
567   if (type.basetype == TypeDesc::STRING && type.aggregate == TypeDesc::SCALAR &&
568       type.arraylen == 0) {
569     ustring *sval = (ustring *)val;
570     sval[0] = str;
571
572     if (derivatives) {
573       sval[1] = OSLRenderServices::u_empty;
574       sval[2] = OSLRenderServices::u_empty;
575     }
576
577     return true;
578   }
579
580   return false;
581 }
582
583 static bool set_attribute_float3_3(float3 P[3], TypeDesc type, bool derivatives, void *val)
584 {
585   if (type.vecsemantics == TypeDesc::POINT && type.arraylen >= 3) {
586     float *fval = (float *)val;
587
588     fval[0] = P[0].x;
589     fval[1] = P[0].y;
590     fval[2] = P[0].z;
591
592     fval[3] = P[1].x;
593     fval[4] = P[1].y;
594     fval[5] = P[1].z;
595
596     fval[6] = P[2].x;
597     fval[7] = P[2].y;
598     fval[8] = P[2].z;
599
600     if (type.arraylen > 3)
601       memset(fval + 3 * 3, 0, sizeof(float) * 3 * (type.arraylen - 3));
602     if (derivatives)
603       memset(fval + type.arraylen * 3, 0, sizeof(float) * 2 * 3 * type.arraylen);
604
605     return true;
606   }
607
608   return false;
609 }
610
611 static bool set_attribute_matrix(const Transform &tfm, TypeDesc type, void *val)
612 {
613   if (type == TypeDesc::TypeMatrix) {
614     copy_matrix(*(OSL::Matrix44 *)val, tfm);
615     return true;
616   }
617
618   return false;
619 }
620
621 static bool get_primitive_attribute(KernelGlobals *kg,
622                                     const ShaderData *sd,
623                                     const OSLGlobals::Attribute &attr,
624                                     const TypeDesc &type,
625                                     bool derivatives,
626                                     void *val)
627 {
628   if (attr.type == TypeDesc::TypePoint || attr.type == TypeDesc::TypeVector ||
629       attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor) {
630     float3 fval[3];
631     fval[0] = primitive_attribute_float3(
632         kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
633     return set_attribute_float3(fval, type, derivatives, val);
634   }
635   else if (attr.type == TypeFloat2) {
636     float2 fval[2];
637     fval[0] = primitive_attribute_float2(
638         kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
639     return set_attribute_float2(fval, type, derivatives, val);
640   }
641   else if (attr.type == TypeDesc::TypeFloat) {
642     float fval[3];
643     fval[0] = primitive_attribute_float(
644         kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
645     return set_attribute_float(fval, type, derivatives, val);
646   }
647   else {
648     return false;
649   }
650 }
651
652 static bool get_mesh_attribute(KernelGlobals *kg,
653                                const ShaderData *sd,
654                                const OSLGlobals::Attribute &attr,
655                                const TypeDesc &type,
656                                bool derivatives,
657                                void *val)
658 {
659   if (attr.type == TypeDesc::TypeMatrix) {
660     Transform tfm = primitive_attribute_matrix(kg, sd, attr.desc);
661     return set_attribute_matrix(tfm, type, val);
662   }
663   else {
664     return false;
665   }
666 }
667
668 static void get_object_attribute(const OSLGlobals::Attribute &attr, bool derivatives, void *val)
669 {
670   size_t datasize = attr.value.datasize();
671
672   memcpy(val, attr.value.data(), datasize);
673   if (derivatives)
674     memset((char *)val + datasize, 0, datasize * 2);
675 }
676
677 bool OSLRenderServices::get_object_standard_attribute(
678     KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val)
679 {
680   /* todo: turn this into hash table? */
681
682   /* Object Attributes */
683   if (name == u_object_location) {
684     float3 f = object_location(kg, sd);
685     return set_attribute_float3(f, type, derivatives, val);
686   }
687   else if (name == u_object_index) {
688     float f = object_pass_id(kg, sd->object);
689     return set_attribute_float(f, type, derivatives, val);
690   }
691   else if (name == u_geom_dupli_generated) {
692     float3 f = object_dupli_generated(kg, sd->object);
693     return set_attribute_float3(f, type, derivatives, val);
694   }
695   else if (name == u_geom_dupli_uv) {
696     float3 f = object_dupli_uv(kg, sd->object);
697     return set_attribute_float3(f, type, derivatives, val);
698   }
699   else if (name == u_material_index) {
700     float f = shader_pass_id(kg, sd);
701     return set_attribute_float(f, type, derivatives, val);
702   }
703   else if (name == u_object_random) {
704     float f = object_random_number(kg, sd->object);
705     return set_attribute_float(f, type, derivatives, val);
706   }
707
708   /* Particle Attributes */
709   else if (name == u_particle_index) {
710     int particle_id = object_particle_id(kg, sd->object);
711     float f = particle_index(kg, particle_id);
712     return set_attribute_float(f, type, derivatives, val);
713   }
714   else if (name == u_particle_random) {
715     int particle_id = object_particle_id(kg, sd->object);
716     float f = hash_int_01(particle_index(kg, particle_id));
717     return set_attribute_float(f, type, derivatives, val);
718   }
719
720   else if (name == u_particle_age) {
721     int particle_id = object_particle_id(kg, sd->object);
722     float f = particle_age(kg, particle_id);
723     return set_attribute_float(f, type, derivatives, val);
724   }
725   else if (name == u_particle_lifetime) {
726     int particle_id = object_particle_id(kg, sd->object);
727     float f = particle_lifetime(kg, particle_id);
728     return set_attribute_float(f, type, derivatives, val);
729   }
730   else if (name == u_particle_location) {
731     int particle_id = object_particle_id(kg, sd->object);
732     float3 f = particle_location(kg, particle_id);
733     return set_attribute_float3(f, type, derivatives, val);
734   }
735 #if 0 /* unsupported */
736   else if (name == u_particle_rotation) {
737     int particle_id = object_particle_id(kg, sd->object);
738     float4 f = particle_rotation(kg, particle_id);
739     return set_attribute_float4(f, type, derivatives, val);
740   }
741 #endif
742   else if (name == u_particle_size) {
743     int particle_id = object_particle_id(kg, sd->object);
744     float f = particle_size(kg, particle_id);
745     return set_attribute_float(f, type, derivatives, val);
746   }
747   else if (name == u_particle_velocity) {
748     int particle_id = object_particle_id(kg, sd->object);
749     float3 f = particle_velocity(kg, particle_id);
750     return set_attribute_float3(f, type, derivatives, val);
751   }
752   else if (name == u_particle_angular_velocity) {
753     int particle_id = object_particle_id(kg, sd->object);
754     float3 f = particle_angular_velocity(kg, particle_id);
755     return set_attribute_float3(f, type, derivatives, val);
756   }
757
758   /* Geometry Attributes */
759   else if (name == u_geom_numpolyvertices) {
760     return set_attribute_int(3, type, derivatives, val);
761   }
762   else if ((name == u_geom_trianglevertices || name == u_geom_polyvertices) &&
763            sd->type & PRIMITIVE_ALL_TRIANGLE) {
764     float3 P[3];
765
766     if (sd->type & PRIMITIVE_TRIANGLE)
767       triangle_vertices(kg, sd->prim, P);
768     else
769       motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, P);
770
771     if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
772       object_position_transform(kg, sd, &P[0]);
773       object_position_transform(kg, sd, &P[1]);
774       object_position_transform(kg, sd, &P[2]);
775     }
776
777     return set_attribute_float3_3(P, type, derivatives, val);
778   }
779   else if (name == u_geom_name) {
780     ustring object_name = kg->osl->object_names[sd->object];
781     return set_attribute_string(object_name, type, derivatives, val);
782   }
783   else if (name == u_is_smooth) {
784     float f = ((sd->shader & SHADER_SMOOTH_NORMAL) != 0);
785     return set_attribute_float(f, type, derivatives, val);
786   }
787   /* Hair Attributes */
788   else if (name == u_is_curve) {
789     float f = (sd->type & PRIMITIVE_ALL_CURVE) != 0;
790     return set_attribute_float(f, type, derivatives, val);
791   }
792   else if (name == u_curve_thickness) {
793     float f = curve_thickness(kg, sd);
794     return set_attribute_float(f, type, derivatives, val);
795   }
796   else if (name == u_curve_tangent_normal) {
797     float3 f = curve_tangent_normal(kg, sd);
798     return set_attribute_float3(f, type, derivatives, val);
799   }
800   else
801     return false;
802 }
803
804 bool OSLRenderServices::get_background_attribute(
805     KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val)
806 {
807   if (name == u_path_ray_length) {
808     /* Ray Length */
809     float f = sd->ray_length;
810     return set_attribute_float(f, type, derivatives, val);
811   }
812   else if (name == u_path_ray_depth) {
813     /* Ray Depth */
814     PathState *state = sd->osl_path_state;
815     int f = state->bounce;
816     return set_attribute_int(f, type, derivatives, val);
817   }
818   else if (name == u_path_diffuse_depth) {
819     /* Diffuse Ray Depth */
820     PathState *state = sd->osl_path_state;
821     int f = state->diffuse_bounce;
822     return set_attribute_int(f, type, derivatives, val);
823   }
824   else if (name == u_path_glossy_depth) {
825     /* Glossy Ray Depth */
826     PathState *state = sd->osl_path_state;
827     int f = state->glossy_bounce;
828     return set_attribute_int(f, type, derivatives, val);
829   }
830   else if (name == u_path_transmission_depth) {
831     /* Transmission Ray Depth */
832     PathState *state = sd->osl_path_state;
833     int f = state->transmission_bounce;
834     return set_attribute_int(f, type, derivatives, val);
835   }
836   else if (name == u_path_transparent_depth) {
837     /* Transparent Ray Depth */
838     PathState *state = sd->osl_path_state;
839     int f = state->transparent_bounce;
840     return set_attribute_int(f, type, derivatives, val);
841   }
842   else if (name == u_path_transmission_depth) {
843     /* Transmission Ray Depth */
844     PathState *state = sd->osl_path_state;
845     int f = state->transmission_bounce;
846     return set_attribute_int(f, type, derivatives, val);
847   }
848   else if (name == u_ndc) {
849     /* NDC coordinates with special exception for otho */
850     OSLThreadData *tdata = kg->osl_tdata;
851     OSL::ShaderGlobals *globals = &tdata->globals;
852     float3 ndc[3];
853
854     if ((globals->raytype & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE &&
855         kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
856       ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P);
857
858       if (derivatives) {
859         ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dx) - ndc[0];
860         ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dy) - ndc[0];
861       }
862     }
863     else {
864       ndc[0] = camera_world_to_ndc(kg, sd, sd->P);
865
866       if (derivatives) {
867         ndc[1] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx) - ndc[0];
868         ndc[2] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy) - ndc[0];
869       }
870     }
871
872     return set_attribute_float3(ndc, type, derivatives, val);
873   }
874   else
875     return false;
876 }
877
878 bool OSLRenderServices::get_attribute(OSL::ShaderGlobals *sg,
879                                       bool derivatives,
880                                       ustring object_name,
881                                       TypeDesc type,
882                                       ustring name,
883                                       void *val)
884 {
885   if (sg == NULL || sg->renderstate == NULL)
886     return false;
887
888   ShaderData *sd = (ShaderData *)(sg->renderstate);
889   return get_attribute(sd, derivatives, object_name, type, name, val);
890 }
891
892 bool OSLRenderServices::get_attribute(
893     ShaderData *sd, bool derivatives, ustring object_name, TypeDesc type, ustring name, void *val)
894 {
895   KernelGlobals *kg = sd->osl_globals;
896   int prim_type = 0;
897   int object;
898
899   /* lookup of attribute on another object */
900   if (object_name != u_empty) {
901     OSLGlobals::ObjectNameMap::iterator it = kg->osl->object_name_map.find(object_name);
902
903     if (it == kg->osl->object_name_map.end())
904       return false;
905
906     object = it->second;
907   }
908   else {
909     object = sd->object;
910     prim_type = attribute_primitive_type(kg, sd);
911
912     if (object == OBJECT_NONE)
913       return get_background_attribute(kg, sd, name, type, derivatives, val);
914   }
915
916   /* find attribute on object */
917   object = object * ATTR_PRIM_TYPES + prim_type;
918   OSLGlobals::AttributeMap &attribute_map = kg->osl->attribute_map[object];
919   OSLGlobals::AttributeMap::iterator it = attribute_map.find(name);
920
921   if (it != attribute_map.end()) {
922     const OSLGlobals::Attribute &attr = it->second;
923
924     if (attr.desc.element != ATTR_ELEMENT_OBJECT) {
925       /* triangle and vertex attributes */
926       if (get_primitive_attribute(kg, sd, attr, type, derivatives, val))
927         return true;
928       else
929         return get_mesh_attribute(kg, sd, attr, type, derivatives, val);
930     }
931     else {
932       /* object attribute */
933       get_object_attribute(attr, derivatives, val);
934       return true;
935     }
936   }
937   else {
938     /* not found in attribute, check standard object info */
939     bool is_std_object_attribute = get_object_standard_attribute(
940         kg, sd, name, type, derivatives, val);
941
942     if (is_std_object_attribute)
943       return true;
944
945     return get_background_attribute(kg, sd, name, type, derivatives, val);
946   }
947
948   return false;
949 }
950
951 bool OSLRenderServices::get_userdata(
952     bool derivatives, ustring name, TypeDesc type, OSL::ShaderGlobals *sg, void *val)
953 {
954   return false; /* disabled by lockgeom */
955 }
956
957 TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring filename)
958 {
959   if (filename.length() && filename[0] == '@') {
960     /* Dummy, we don't use texture handles for builtin textures but need
961      * to tell the OSL runtime optimizer that this is a valid texture. */
962     return NULL;
963   }
964   else {
965     return texturesys()->get_texture_handle(filename);
966   }
967 }
968
969 bool OSLRenderServices::good(TextureSystem::TextureHandle *texture_handle)
970 {
971   return texturesys()->good(texture_handle);
972 }
973
974 bool OSLRenderServices::texture(ustring filename,
975                                 TextureHandle *texture_handle,
976                                 TexturePerthread *texture_thread_info,
977                                 TextureOpt &options,
978                                 OSL::ShaderGlobals *sg,
979                                 float s,
980                                 float t,
981                                 float dsdx,
982                                 float dtdx,
983                                 float dsdy,
984                                 float dtdy,
985                                 int nchannels,
986                                 float *result,
987                                 float *dresultds,
988                                 float *dresultdt,
989                                 ustring *errormessage)
990 {
991   OSL::TextureSystem *ts = osl_ts;
992   ShaderData *sd = (ShaderData *)(sg->renderstate);
993   KernelGlobals *kg = sd->osl_globals;
994
995   if (texture_thread_info == NULL) {
996     OSLThreadData *tdata = kg->osl_tdata;
997     texture_thread_info = tdata->oiio_thread_info;
998   }
999
1000 #ifdef WITH_PTEX
1001   /* todo: this is just a quick hack, only works with particular files and options */
1002   if (string_endswith(filename.string(), ".ptx")) {
1003     float2 uv;
1004     int faceid;
1005
1006     if (!primitive_ptex(kg, sd, &uv, &faceid))
1007       return false;
1008
1009     float u = uv.x;
1010     float v = uv.y;
1011     float dudx = 0.0f;
1012     float dvdx = 0.0f;
1013     float dudy = 0.0f;
1014     float dvdy = 0.0f;
1015
1016     Ptex::String error;
1017     PtexPtr<PtexTexture> r(ptex_cache->get(filename.c_str(), error));
1018
1019     if (!r) {
1020       //std::cerr << error.c_str() << std::endl;
1021       return false;
1022     }
1023
1024     bool mipmaplerp = false;
1025     float sharpness = 1.0f;
1026     PtexFilter::Options opts(PtexFilter::f_bicubic, mipmaplerp, sharpness);
1027     PtexPtr<PtexFilter> f(PtexFilter::getFilter(r, opts));
1028
1029     f->eval(result, options.firstchannel, nchannels, faceid, u, v, dudx, dvdx, dudy, dvdy);
1030
1031     for (int c = r->numChannels(); c < nchannels; c++)
1032       result[c] = result[0];
1033
1034     return true;
1035   }
1036 #endif
1037   bool status = false;
1038
1039   if (filename.length() && filename[0] == '@') {
1040     if (filename == u_at_bevel) {
1041       /* Bevel shader hack. */
1042       if (nchannels >= 3) {
1043         PathState *state = sd->osl_path_state;
1044         int num_samples = (int)s;
1045         float radius = t;
1046         float3 N = svm_bevel(kg, sd, state, radius, num_samples);
1047         result[0] = N.x;
1048         result[1] = N.y;
1049         result[2] = N.z;
1050         status = true;
1051       }
1052     }
1053     else if (filename == u_at_ao) {
1054       /* AO shader hack. */
1055       PathState *state = sd->osl_path_state;
1056       int num_samples = (int)s;
1057       float radius = t;
1058       float3 N = make_float3(dsdx, dtdx, dsdy);
1059       int flags = 0;
1060       if ((int)dtdy) {
1061         flags |= NODE_AO_INSIDE;
1062       }
1063       if ((int)options.sblur) {
1064         flags |= NODE_AO_ONLY_LOCAL;
1065       }
1066       if ((int)options.tblur) {
1067         flags |= NODE_AO_GLOBAL_RADIUS;
1068       }
1069       result[0] = svm_ao(kg, sd, N, state, radius, num_samples, flags);
1070       status = true;
1071     }
1072     else if (filename[1] == 'l') {
1073       /* IES light. */
1074       int slot = atoi(filename.c_str() + 2);
1075       result[0] = kernel_ies_interp(kg, slot, s, t);
1076       status = true;
1077     }
1078     else {
1079       /* Packed texture. */
1080       int slot = atoi(filename.c_str() + 2);
1081       float4 rgba = kernel_tex_image_interp(kg, slot, s, 1.0f - t);
1082
1083       result[0] = rgba[0];
1084       if (nchannels > 1)
1085         result[1] = rgba[1];
1086       if (nchannels > 2)
1087         result[2] = rgba[2];
1088       if (nchannels > 3)
1089         result[3] = rgba[3];
1090       status = true;
1091     }
1092   }
1093   else {
1094     if (texture_handle != NULL) {
1095       status = ts->texture(texture_handle,
1096                            texture_thread_info,
1097                            options,
1098                            s,
1099                            t,
1100                            dsdx,
1101                            dtdx,
1102                            dsdy,
1103                            dtdy,
1104                            nchannels,
1105                            result,
1106                            dresultds,
1107                            dresultdt);
1108     }
1109     else {
1110       status = ts->texture(filename,
1111                            options,
1112                            s,
1113                            t,
1114                            dsdx,
1115                            dtdx,
1116                            dsdy,
1117                            dtdy,
1118                            nchannels,
1119                            result,
1120                            dresultds,
1121                            dresultdt);
1122     }
1123   }
1124
1125   if (!status) {
1126     if (nchannels == 3 || nchannels == 4) {
1127       result[0] = 1.0f;
1128       result[1] = 0.0f;
1129       result[2] = 1.0f;
1130
1131       if (nchannels == 4)
1132         result[3] = 1.0f;
1133     }
1134     /* This might be slow, but prevents error messages leak and
1135      * other nasty stuff happening.
1136      */
1137     string err = ts->geterror();
1138     (void)err;
1139   }
1140
1141   return status;
1142 }
1143
1144 bool OSLRenderServices::texture3d(ustring filename,
1145                                   TextureHandle *texture_handle,
1146                                   TexturePerthread *texture_thread_info,
1147                                   TextureOpt &options,
1148                                   OSL::ShaderGlobals *sg,
1149                                   const OSL::Vec3 &P,
1150                                   const OSL::Vec3 &dPdx,
1151                                   const OSL::Vec3 &dPdy,
1152                                   const OSL::Vec3 &dPdz,
1153                                   int nchannels,
1154                                   float *result,
1155                                   float *dresultds,
1156                                   float *dresultdt,
1157                                   float *dresultdr,
1158                                   ustring *errormessage)
1159 {
1160   OSL::TextureSystem *ts = osl_ts;
1161   ShaderData *sd = (ShaderData *)(sg->renderstate);
1162   KernelGlobals *kg = sd->osl_globals;
1163
1164   if (texture_thread_info == NULL) {
1165     OSLThreadData *tdata = kg->osl_tdata;
1166     texture_thread_info = tdata->oiio_thread_info;
1167   }
1168
1169   bool status;
1170   if (filename.length() && filename[0] == '@') {
1171     int slot = atoi(filename.c_str() + 1);
1172     float4 rgba = kernel_tex_image_interp_3d(kg, slot, P.x, P.y, P.z, INTERPOLATION_NONE);
1173
1174     result[0] = rgba[0];
1175     if (nchannels > 1)
1176       result[1] = rgba[1];
1177     if (nchannels > 2)
1178       result[2] = rgba[2];
1179     if (nchannels > 3)
1180       result[3] = rgba[3];
1181     status = true;
1182   }
1183   else {
1184     if (texture_handle != NULL) {
1185       status = ts->texture3d(texture_handle,
1186                              texture_thread_info,
1187                              options,
1188                              P,
1189                              dPdx,
1190                              dPdy,
1191                              dPdz,
1192                              nchannels,
1193                              result,
1194                              dresultds,
1195                              dresultdt,
1196                              dresultdr);
1197     }
1198     else {
1199       status = ts->texture3d(filename,
1200                              options,
1201                              P,
1202                              dPdx,
1203                              dPdy,
1204                              dPdz,
1205                              nchannels,
1206                              result,
1207                              dresultds,
1208                              dresultdt,
1209                              dresultdr);
1210     }
1211   }
1212
1213   if (!status) {
1214     if (nchannels == 3 || nchannels == 4) {
1215       result[0] = 1.0f;
1216       result[1] = 0.0f;
1217       result[2] = 1.0f;
1218
1219       if (nchannels == 4)
1220         result[3] = 1.0f;
1221     }
1222     /* This might be slow, but prevents error messages leak and
1223      * other nasty stuff happening.
1224      */
1225     string err = ts->geterror();
1226     (void)err;
1227   }
1228
1229   return status;
1230 }
1231
1232 bool OSLRenderServices::environment(ustring filename,
1233                                     TextureHandle *th,
1234                                     TexturePerthread *thread_info,
1235                                     TextureOpt &options,
1236                                     OSL::ShaderGlobals *sg,
1237                                     const OSL::Vec3 &R,
1238                                     const OSL::Vec3 &dRdx,
1239                                     const OSL::Vec3 &dRdy,
1240                                     int nchannels,
1241                                     float *result,
1242                                     float *dresultds,
1243                                     float *dresultdt,
1244                                     ustring *errormessage)
1245 {
1246   OSL::TextureSystem *ts = osl_ts;
1247
1248   if (thread_info == NULL) {
1249     ShaderData *sd = (ShaderData *)(sg->renderstate);
1250     KernelGlobals *kg = sd->osl_globals;
1251     OSLThreadData *tdata = kg->osl_tdata;
1252     thread_info = tdata->oiio_thread_info;
1253   }
1254
1255   if (th == NULL) {
1256     th = ts->get_texture_handle(filename, thread_info);
1257   }
1258
1259   bool status = ts->environment(
1260       th, thread_info, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt);
1261
1262   if (!status) {
1263     if (nchannels == 3 || nchannels == 4) {
1264       result[0] = 1.0f;
1265       result[1] = 0.0f;
1266       result[2] = 1.0f;
1267
1268       if (nchannels == 4)
1269         result[3] = 1.0f;
1270     }
1271   }
1272
1273   return status;
1274 }
1275
1276 bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg,
1277                                          ustring filename,
1278                                          TextureHandle *th,
1279                                          int subimage,
1280                                          ustring dataname,
1281                                          TypeDesc datatype,
1282                                          void *data)
1283 {
1284   OSL::TextureSystem *ts = osl_ts;
1285   if (filename.length() && filename[0] == '@') {
1286     /* Special builtin textures. */
1287     return false;
1288   }
1289   else {
1290     return ts->get_texture_info(filename, subimage, dataname, datatype, data);
1291   }
1292 }
1293
1294 int OSLRenderServices::pointcloud_search(OSL::ShaderGlobals *sg,
1295                                          ustring filename,
1296                                          const OSL::Vec3 &center,
1297                                          float radius,
1298                                          int max_points,
1299                                          bool sort,
1300                                          size_t *out_indices,
1301                                          float *out_distances,
1302                                          int derivs_offset)
1303 {
1304   return 0;
1305 }
1306
1307 int OSLRenderServices::pointcloud_get(OSL::ShaderGlobals *sg,
1308                                       ustring filename,
1309                                       size_t *indices,
1310                                       int count,
1311                                       ustring attr_name,
1312                                       TypeDesc attr_type,
1313                                       void *out_data)
1314 {
1315   return 0;
1316 }
1317
1318 bool OSLRenderServices::pointcloud_write(OSL::ShaderGlobals *sg,
1319                                          ustring filename,
1320                                          const OSL::Vec3 &pos,
1321                                          int nattribs,
1322                                          const ustring *names,
1323                                          const TypeDesc *types,
1324                                          const void **data)
1325 {
1326   return false;
1327 }
1328
1329 bool OSLRenderServices::trace(TraceOpt &options,
1330                               OSL::ShaderGlobals *sg,
1331                               const OSL::Vec3 &P,
1332                               const OSL::Vec3 &dPdx,
1333                               const OSL::Vec3 &dPdy,
1334                               const OSL::Vec3 &R,
1335                               const OSL::Vec3 &dRdx,
1336                               const OSL::Vec3 &dRdy)
1337 {
1338   /* todo: options.shader support, maybe options.traceset */
1339   ShaderData *sd = (ShaderData *)(sg->renderstate);
1340
1341   /* setup ray */
1342   Ray ray;
1343
1344   ray.P = TO_FLOAT3(P);
1345   ray.D = TO_FLOAT3(R);
1346   ray.t = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist;
1347   ray.time = sd->time;
1348
1349   if (options.mindist == 0.0f) {
1350     /* avoid self-intersections */
1351     if (ray.P == sd->P) {
1352       bool transmit = (dot(sd->Ng, ray.D) < 0.0f);
1353       ray.P = ray_offset(sd->P, (transmit) ? -sd->Ng : sd->Ng);
1354     }
1355   }
1356   else {
1357     /* offset for minimum distance */
1358     ray.P += options.mindist * ray.D;
1359   }
1360
1361   /* ray differentials */
1362   ray.dP.dx = TO_FLOAT3(dPdx);
1363   ray.dP.dy = TO_FLOAT3(dPdy);
1364   ray.dD.dx = TO_FLOAT3(dRdx);
1365   ray.dD.dy = TO_FLOAT3(dRdy);
1366
1367   /* allocate trace data */
1368   OSLTraceData *tracedata = (OSLTraceData *)sg->tracedata;
1369   tracedata->ray = ray;
1370   tracedata->setup = false;
1371   tracedata->init = true;
1372   tracedata->sd.osl_globals = sd->osl_globals;
1373
1374   /* Raytrace, leaving out shadow opaque to avoid early exit. */
1375   uint visibility = PATH_RAY_ALL_VISIBILITY - PATH_RAY_SHADOW_OPAQUE;
1376   return scene_intersect(sd->osl_globals, ray, visibility, &tracedata->isect, NULL, 0.0f, 0.0f);
1377 }
1378
1379 bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg,
1380                                    ustring source,
1381                                    ustring name,
1382                                    TypeDesc type,
1383                                    void *val,
1384                                    bool derivatives)
1385 {
1386   OSLTraceData *tracedata = (OSLTraceData *)sg->tracedata;
1387
1388   if (source == u_trace && tracedata->init) {
1389     if (name == u_hit) {
1390       return set_attribute_int((tracedata->isect.prim != PRIM_NONE), type, derivatives, val);
1391     }
1392     else if (tracedata->isect.prim != PRIM_NONE) {
1393       if (name == u_hitdist) {
1394         float f[3] = {tracedata->isect.t, 0.0f, 0.0f};
1395         return set_attribute_float(f, type, derivatives, val);
1396       }
1397       else {
1398         ShaderData *sd = &tracedata->sd;
1399         KernelGlobals *kg = sd->osl_globals;
1400
1401         if (!tracedata->setup) {
1402           /* lazy shader data setup */
1403           shader_setup_from_ray(kg, sd, &tracedata->isect, &tracedata->ray);
1404           tracedata->setup = true;
1405         }
1406
1407         if (name == u_N) {
1408           return set_attribute_float3(sd->N, type, derivatives, val);
1409         }
1410         else if (name == u_Ng) {
1411           return set_attribute_float3(sd->Ng, type, derivatives, val);
1412         }
1413         else if (name == u_P) {
1414           float3 f[3] = {sd->P, sd->dP.dx, sd->dP.dy};
1415           return set_attribute_float3(f, type, derivatives, val);
1416         }
1417         else if (name == u_I) {
1418           float3 f[3] = {sd->I, sd->dI.dx, sd->dI.dy};
1419           return set_attribute_float3(f, type, derivatives, val);
1420         }
1421         else if (name == u_u) {
1422           float f[3] = {sd->u, sd->du.dx, sd->du.dy};
1423           return set_attribute_float(f, type, derivatives, val);
1424         }
1425         else if (name == u_v) {
1426           float f[3] = {sd->v, sd->dv.dx, sd->dv.dy};
1427           return set_attribute_float(f, type, derivatives, val);
1428         }
1429
1430         return get_attribute(sd, derivatives, u_empty, type, name, val);
1431       }
1432     }
1433   }
1434
1435   return false;
1436 }
1437
1438 CCL_NAMESPACE_END