Cycles: Remove ccl_fetch and SOA
[blender.git] / intern / cycles / kernel / geom / geom_object.h
1 /*
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at
5  *
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14
15 /* Object Primitive
16  *
17  * All mesh and curve primitives are part of an object. The same mesh and curves
18  * may be instanced multiple times by different objects.
19  *
20  * If the mesh is not instanced multiple times, the object will not be explicitly
21  * stored as a primitive in the BVH, rather the bare triangles are curved are
22  * directly primitives in the BVH with world space locations applied, and the object
23  * ID is looked up afterwards. */
24
25 CCL_NAMESPACE_BEGIN
26
27 /* Object attributes, for now a fixed size and contents */
28
29 enum ObjectTransform {
30         OBJECT_TRANSFORM = 0,
31         OBJECT_TRANSFORM_MOTION_PRE = 0,
32         OBJECT_INVERSE_TRANSFORM = 4,
33         OBJECT_TRANSFORM_MOTION_POST = 4,
34         OBJECT_PROPERTIES = 8,
35         OBJECT_DUPLI = 9
36 };
37
38 enum ObjectVectorTransform {
39         OBJECT_VECTOR_MOTION_PRE = 0,
40         OBJECT_VECTOR_MOTION_POST = 3
41 };
42
43 /* Object to world space transformation */
44
45 ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object, enum ObjectTransform type)
46 {
47         int offset = object*OBJECT_SIZE + (int)type;
48
49         Transform tfm;
50         tfm.x = kernel_tex_fetch(__objects, offset + 0);
51         tfm.y = kernel_tex_fetch(__objects, offset + 1);
52         tfm.z = kernel_tex_fetch(__objects, offset + 2);
53         tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
54
55         return tfm;
56 }
57
58 /* Lamp to world space transformation */
59
60 ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse)
61 {
62         int offset = lamp*LIGHT_SIZE + (inverse? 8 : 5);
63
64         Transform tfm;
65         tfm.x = kernel_tex_fetch(__light_data, offset + 0);
66         tfm.y = kernel_tex_fetch(__light_data, offset + 1);
67         tfm.z = kernel_tex_fetch(__light_data, offset + 2);
68         tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
69
70         return tfm;
71 }
72
73 /* Object to world space transformation for motion vectors */
74
75 ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type)
76 {
77         int offset = object*OBJECT_VECTOR_SIZE + (int)type;
78
79         Transform tfm;
80         tfm.x = kernel_tex_fetch(__objects_vector, offset + 0);
81         tfm.y = kernel_tex_fetch(__objects_vector, offset + 1);
82         tfm.z = kernel_tex_fetch(__objects_vector, offset + 2);
83         tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
84
85         return tfm;
86 }
87
88 /* Motion blurred object transformations */
89
90 #ifdef __OBJECT_MOTION__
91 ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int object, float time)
92 {
93         DecompMotionTransform motion;
94
95         int offset = object*OBJECT_SIZE + (int)OBJECT_TRANSFORM_MOTION_PRE;
96
97         motion.mid.x = kernel_tex_fetch(__objects, offset + 0);
98         motion.mid.y = kernel_tex_fetch(__objects, offset + 1);
99         motion.mid.z = kernel_tex_fetch(__objects, offset + 2);
100         motion.mid.w = kernel_tex_fetch(__objects, offset + 3);
101
102         motion.pre_x = kernel_tex_fetch(__objects, offset + 4);
103         motion.pre_y = kernel_tex_fetch(__objects, offset + 5);
104         motion.post_x = kernel_tex_fetch(__objects, offset + 6);
105         motion.post_y = kernel_tex_fetch(__objects, offset + 7);
106
107         Transform tfm;
108         transform_motion_interpolate(&tfm, &motion, time);
109
110         return tfm;
111 }
112
113 ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg, int object, float time, Transform *itfm)
114 {
115         int object_flag = kernel_tex_fetch(__object_flag, object);
116         if(object_flag & SD_OBJECT_MOTION) {
117                 /* if we do motion blur */
118                 Transform tfm = object_fetch_transform_motion(kg, object, time);
119
120                 if(itfm)
121                         *itfm = transform_quick_inverse(tfm);
122
123                 return tfm;
124         }
125         else {
126                 Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
127                 if(itfm)
128                         *itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
129
130                 return tfm;
131         }
132 }
133 #endif
134
135 /* Transform position from object to world space */
136
137 ccl_device_inline void object_position_transform(KernelGlobals *kg, const ShaderData *sd, float3 *P)
138 {
139 #ifdef __OBJECT_MOTION__
140         *P = transform_point_auto(&sd->ob_tfm, *P);
141 #else
142         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
143         *P = transform_point(&tfm, *P);
144 #endif
145 }
146
147 /* Transform position from world to object space */
148
149 ccl_device_inline void object_inverse_position_transform(KernelGlobals *kg, const ShaderData *sd, float3 *P)
150 {
151 #ifdef __OBJECT_MOTION__
152         *P = transform_point_auto(&sd->ob_itfm, *P);
153 #else
154         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
155         *P = transform_point(&tfm, *P);
156 #endif
157 }
158
159 /* Transform normal from world to object space */
160
161 ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, const ShaderData *sd, float3 *N)
162 {
163 #ifdef __OBJECT_MOTION__
164         if((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) {
165                 *N = normalize(transform_direction_transposed_auto(&sd->ob_tfm, *N));
166         }
167 #else
168         if(sd->object != OBJECT_NONE) {
169                 Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
170                 *N = normalize(transform_direction_transposed(&tfm, *N));
171         }
172 #endif
173 }
174
175 /* Transform normal from object to world space */
176
177 ccl_device_inline void object_normal_transform(KernelGlobals *kg, const ShaderData *sd, float3 *N)
178 {
179 #ifdef __OBJECT_MOTION__
180         *N = normalize(transform_direction_transposed_auto(&sd->ob_itfm, *N));
181 #else
182         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
183         *N = normalize(transform_direction_transposed(&tfm, *N));
184 #endif
185 }
186
187 /* Transform direction vector from object to world space */
188
189 ccl_device_inline void object_dir_transform(KernelGlobals *kg, const ShaderData *sd, float3 *D)
190 {
191 #ifdef __OBJECT_MOTION__
192         *D = transform_direction_auto(&sd->ob_tfm, *D);
193 #else
194         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
195         *D = transform_direction(&tfm, *D);
196 #endif
197 }
198
199 /* Transform direction vector from world to object space */
200
201 ccl_device_inline void object_inverse_dir_transform(KernelGlobals *kg, const ShaderData *sd, float3 *D)
202 {
203 #ifdef __OBJECT_MOTION__
204         *D = transform_direction_auto(&sd->ob_itfm, *D);
205 #else
206         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
207         *D = transform_direction(&tfm, *D);
208 #endif
209 }
210
211 /* Object center position */
212
213 ccl_device_inline float3 object_location(KernelGlobals *kg, const ShaderData *sd)
214 {
215         if(sd->object == OBJECT_NONE)
216                 return make_float3(0.0f, 0.0f, 0.0f);
217
218 #ifdef __OBJECT_MOTION__
219         return make_float3(sd->ob_tfm.x.w, sd->ob_tfm.y.w, sd->ob_tfm.z.w);
220 #else
221         Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
222         return make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
223 #endif
224 }
225
226 /* Total surface area of object */
227
228 ccl_device_inline float object_surface_area(KernelGlobals *kg, int object)
229 {
230         int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
231         float4 f = kernel_tex_fetch(__objects, offset);
232         return f.x;
233 }
234
235 /* Pass ID number of object */
236
237 ccl_device_inline float object_pass_id(KernelGlobals *kg, int object)
238 {
239         if(object == OBJECT_NONE)
240                 return 0.0f;
241
242         int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
243         float4 f = kernel_tex_fetch(__objects, offset);
244         return f.y;
245 }
246
247 /* Per object random number for shader variation */
248
249 ccl_device_inline float object_random_number(KernelGlobals *kg, int object)
250 {
251         if(object == OBJECT_NONE)
252                 return 0.0f;
253
254         int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
255         float4 f = kernel_tex_fetch(__objects, offset);
256         return f.z;
257 }
258
259 /* Particle ID from which this object was generated */
260
261 ccl_device_inline int object_particle_id(KernelGlobals *kg, int object)
262 {
263         if(object == OBJECT_NONE)
264                 return 0;
265
266         int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
267         float4 f = kernel_tex_fetch(__objects, offset);
268         return __float_as_uint(f.w);
269 }
270
271 /* Generated texture coordinate on surface from where object was instanced */
272
273 ccl_device_inline float3 object_dupli_generated(KernelGlobals *kg, int object)
274 {
275         if(object == OBJECT_NONE)
276                 return make_float3(0.0f, 0.0f, 0.0f);
277
278         int offset = object*OBJECT_SIZE + OBJECT_DUPLI;
279         float4 f = kernel_tex_fetch(__objects, offset);
280         return make_float3(f.x, f.y, f.z);
281 }
282
283 /* UV texture coordinate on surface from where object was instanced */
284
285 ccl_device_inline float3 object_dupli_uv(KernelGlobals *kg, int object)
286 {
287         if(object == OBJECT_NONE)
288                 return make_float3(0.0f, 0.0f, 0.0f);
289
290         int offset = object*OBJECT_SIZE + OBJECT_DUPLI;
291         float4 f = kernel_tex_fetch(__objects, offset + 1);
292         return make_float3(f.x, f.y, 0.0f);
293 }
294
295 /* Information about mesh for motion blurred triangles and curves */
296
297 ccl_device_inline void object_motion_info(KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys)
298 {
299         int offset = object*OBJECT_SIZE + OBJECT_DUPLI;
300
301         if(numkeys) {
302                 float4 f = kernel_tex_fetch(__objects, offset);
303                 *numkeys = __float_as_int(f.w);
304         }
305
306         float4 f = kernel_tex_fetch(__objects, offset + 1);
307         if(numsteps)
308                 *numsteps = __float_as_int(f.z);
309         if(numverts)
310                 *numverts = __float_as_int(f.w);
311 }
312
313 /* Offset to an objects patch map */
314
315 ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object)
316 {
317         if(object == OBJECT_NONE)
318                 return 0;
319
320         int offset = object*OBJECT_SIZE + 11;
321         float4 f = kernel_tex_fetch(__objects, offset);
322         return __float_as_uint(f.x);
323 }
324
325 /* Pass ID for shader */
326
327 ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd)
328 {
329         return kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE + 1);
330 }
331
332 /* Particle data from which object was instanced */
333
334 ccl_device_inline float particle_index(KernelGlobals *kg, int particle)
335 {
336         int offset = particle*PARTICLE_SIZE;
337         float4 f = kernel_tex_fetch(__particles, offset + 0);
338         return f.x;
339 }
340
341 ccl_device float particle_age(KernelGlobals *kg, int particle)
342 {
343         int offset = particle*PARTICLE_SIZE;
344         float4 f = kernel_tex_fetch(__particles, offset + 0);
345         return f.y;
346 }
347
348 ccl_device float particle_lifetime(KernelGlobals *kg, int particle)
349 {
350         int offset = particle*PARTICLE_SIZE;
351         float4 f = kernel_tex_fetch(__particles, offset + 0);
352         return f.z;
353 }
354
355 ccl_device float particle_size(KernelGlobals *kg, int particle)
356 {
357         int offset = particle*PARTICLE_SIZE;
358         float4 f = kernel_tex_fetch(__particles, offset + 0);
359         return f.w;
360 }
361
362 ccl_device float4 particle_rotation(KernelGlobals *kg, int particle)
363 {
364         int offset = particle*PARTICLE_SIZE;
365         float4 f = kernel_tex_fetch(__particles, offset + 1);
366         return f;
367 }
368
369 ccl_device float3 particle_location(KernelGlobals *kg, int particle)
370 {
371         int offset = particle*PARTICLE_SIZE;
372         float4 f = kernel_tex_fetch(__particles, offset + 2);
373         return make_float3(f.x, f.y, f.z);
374 }
375
376 ccl_device float3 particle_velocity(KernelGlobals *kg, int particle)
377 {
378         int offset = particle*PARTICLE_SIZE;
379         float4 f2 = kernel_tex_fetch(__particles, offset + 2);
380         float4 f3 = kernel_tex_fetch(__particles, offset + 3);
381         return make_float3(f2.w, f3.x, f3.y);
382 }
383
384 ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle)
385 {
386         int offset = particle*PARTICLE_SIZE;
387         float4 f3 = kernel_tex_fetch(__particles, offset + 3);
388         float4 f4 = kernel_tex_fetch(__particles, offset + 4);
389         return make_float3(f3.z, f3.w, f4.x);
390 }
391
392 /* Object intersection in BVH */
393
394 ccl_device_inline float3 bvh_clamp_direction(float3 dir)
395 {
396         /* clamp absolute values by exp2f(-80.0f) to avoid division by zero when calculating inverse direction */
397 #if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__)
398         const ssef oopes(8.271806E-25f,8.271806E-25f,8.271806E-25f,0.0f);
399         const ssef mask = _mm_cmpgt_ps(fabs(dir), oopes);
400         const ssef signdir = signmsk(dir.m128) | oopes;
401 #  ifndef __KERNEL_AVX__
402         ssef res = mask & ssef(dir);
403         res = _mm_or_ps(res,_mm_andnot_ps(mask, signdir));
404 #  else
405         ssef res = _mm_blendv_ps(signdir, dir, mask);
406 #  endif
407         return float3(res);
408 #else  /* __KERNEL_SSE__ && __KERNEL_SSE2__ */
409         const float ooeps = 8.271806E-25f;
410         return make_float3((fabsf(dir.x) > ooeps)? dir.x: copysignf(ooeps, dir.x),
411                            (fabsf(dir.y) > ooeps)? dir.y: copysignf(ooeps, dir.y),
412                            (fabsf(dir.z) > ooeps)? dir.z: copysignf(ooeps, dir.z));
413 #endif  /* __KERNEL_SSE__ && __KERNEL_SSE2__ */
414 }
415
416 ccl_device_inline float3 bvh_inverse_direction(float3 dir)
417 {
418         /* TODO(sergey): Currently disabled, gives speedup but causes precision issues. */
419 #if defined(__KERNEL_SSE__) && 0
420         return rcp(dir);
421 #else
422         return 1.0f / dir;
423 #endif
424 }
425
426 /* Transform ray into object space to enter static object in BVH */
427
428 ccl_device_inline void bvh_instance_push(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, ccl_addr_space float *t)
429 {
430         Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
431
432         *P = transform_point(&tfm, ray->P);
433
434         float len;
435         *dir = bvh_clamp_direction(normalize_len(transform_direction(&tfm, ray->D), &len));
436         *idir = bvh_inverse_direction(*dir);
437
438         if(*t != FLT_MAX)
439                 *t *= len;
440 }
441
442 #ifdef __QBVH__
443 /* Same as above, but optimized for QBVH scene intersection,
444  * which needs to modify two max distances.
445  *
446  * TODO(sergey): Investigate if passing NULL instead of t1 gets optimized
447  * so we can avoid having this duplication.
448  */
449 ccl_device_inline void qbvh_instance_push(KernelGlobals *kg,
450                                           int object,
451                                           const Ray *ray,
452                                           float3 *P,
453                                           float3 *dir,
454                                           float3 *idir,
455                                           float *t,
456                                           float *t1)
457 {
458         Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
459
460         *P = transform_point(&tfm, ray->P);
461
462         float len;
463         *dir = bvh_clamp_direction(normalize_len(transform_direction(&tfm, ray->D), &len));
464         *idir = bvh_inverse_direction(*dir);
465
466         if(*t != FLT_MAX)
467                 *t *= len;
468
469         if(*t1 != -FLT_MAX)
470                 *t1 *= len;
471 }
472 #endif
473
474 /* Transorm ray to exit static object in BVH */
475
476 ccl_device_inline void bvh_instance_pop(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, ccl_addr_space float *t)
477 {
478         if(*t != FLT_MAX) {
479                 Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
480                 *t /= len(transform_direction(&tfm, ray->D));
481         }
482
483         *P = ray->P;
484         *dir = bvh_clamp_direction(ray->D);
485         *idir = bvh_inverse_direction(*dir);
486 }
487
488 /* Same as above, but returns scale factor to apply to multiple intersection distances */
489
490 ccl_device_inline void bvh_instance_pop_factor(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, float *t_fac)
491 {
492         Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
493         *t_fac = 1.0f / len(transform_direction(&tfm, ray->D));
494
495         *P = ray->P;
496         *dir = bvh_clamp_direction(ray->D);
497         *idir = bvh_inverse_direction(*dir);
498 }
499
500
501 #ifdef __OBJECT_MOTION__
502 /* Transform ray into object space to enter motion blurred object in BVH */
503
504 ccl_device_inline void bvh_instance_motion_push(KernelGlobals *kg,
505                                                 int object,
506                                                 const Ray *ray,
507                                                 float3 *P,
508                                                 float3 *dir,
509                                                 float3 *idir,
510                                                 ccl_addr_space float *t,
511                                                 Transform *itfm)
512 {
513         object_fetch_transform_motion_test(kg, object, ray->time, itfm);
514
515         *P = transform_point(itfm, ray->P);
516
517         float len;
518         *dir = bvh_clamp_direction(normalize_len(transform_direction(itfm, ray->D), &len));
519         *idir = bvh_inverse_direction(*dir);
520
521         if(*t != FLT_MAX)
522                 *t *= len;
523 }
524
525 #ifdef __QBVH__
526 /* Same as above, but optimized for QBVH scene intersection,
527  * which needs to modify two max distances.
528  *
529  * TODO(sergey): Investigate if passing NULL instead of t1 gets optimized
530  * so we can avoid having this duplication.
531  */
532 ccl_device_inline void qbvh_instance_motion_push(KernelGlobals *kg,
533                                                  int object,
534                                                  const Ray *ray,
535                                                  float3 *P,
536                                                  float3 *dir,
537                                                  float3 *idir,
538                                                  float *t,
539                                                  float *t1,
540                                                  Transform *itfm)
541 {
542         object_fetch_transform_motion_test(kg, object, ray->time, itfm);
543
544         *P = transform_point(itfm, ray->P);
545
546         float len;
547         *dir = bvh_clamp_direction(normalize_len(transform_direction(itfm, ray->D), &len));
548         *idir = bvh_inverse_direction(*dir);
549
550         if(*t != FLT_MAX)
551                 *t *= len;
552
553         if(*t1 != -FLT_MAX)
554                 *t1 *= len;
555 }
556 #endif
557
558 /* Transorm ray to exit motion blurred object in BVH */
559
560 ccl_device_inline void bvh_instance_motion_pop(KernelGlobals *kg,
561                                                int object,
562                                                const Ray *ray,
563                                                float3 *P,
564                                                float3 *dir,
565                                                float3 *idir,
566                                                ccl_addr_space float *t,
567                                                Transform *itfm)
568 {
569         if(*t != FLT_MAX) {
570                 *t /= len(transform_direction(itfm, ray->D));
571         }
572
573         *P = ray->P;
574         *dir = bvh_clamp_direction(ray->D);
575         *idir = bvh_inverse_direction(*dir);
576 }
577
578 /* Same as above, but returns scale factor to apply to multiple intersection distances */
579
580 ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals *kg,
581                                                       int object,
582                                                       const Ray *ray,
583                                                       float3 *P,
584                                                       float3 *dir,
585                                                       float3 *idir,
586                                                       float *t_fac,
587                                                       Transform *itfm)
588 {
589         *t_fac = 1.0f / len(transform_direction(itfm, ray->D));
590         *P = ray->P;
591         *dir = bvh_clamp_direction(ray->D);
592         *idir = bvh_inverse_direction(*dir);
593 }
594
595 #endif
596
597 /* TODO(sergey): This is only for until we've got OpenCL 2.0
598  * on all devices we consider supported. It'll be replaced with
599  * generic address space.
600  */
601
602 #ifdef __KERNEL_OPENCL__
603 ccl_device_inline void object_position_transform_addrspace(KernelGlobals *kg,
604                                                          const ShaderData *sd,
605                                                          ccl_addr_space float3 *P)
606 {
607         float3 private_P = *P;
608         object_position_transform(kg, sd, &private_P);
609         *P = private_P;
610 }
611
612 ccl_device_inline void object_dir_transform_addrspace(KernelGlobals *kg,
613                                                       const ShaderData *sd,
614                                                       ccl_addr_space float3 *D)
615 {
616         float3 private_D = *D;
617         object_dir_transform(kg, sd, &private_D);
618         *D = private_D;
619 }
620
621 ccl_device_inline void object_normal_transform_addrspace(KernelGlobals *kg,
622                                                          const ShaderData *sd,
623                                                          ccl_addr_space float3 *N)
624 {
625         float3 private_N = *N;
626         object_normal_transform(kg, sd, &private_N);
627         *N = private_N;
628 }
629 #endif
630
631 #ifndef __KERNEL_OPENCL__
632 #  define object_position_transform_auto object_position_transform
633 #  define object_dir_transform_auto object_dir_transform
634 #  define object_normal_transform_auto object_normal_transform
635 #else
636 #  define object_position_transform_auto object_position_transform_addrspace
637 #  define object_dir_transform_auto object_dir_transform_addrspace
638 #  define object_normal_transform_auto object_normal_transform_addrspace
639 #endif
640
641 CCL_NAMESPACE_END
642