style cleanup: block comments
[blender.git] / intern / cycles / kernel / osl / osl_services.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <string.h>
20
21 #include "mesh.h"
22 #include "object.h"
23 #include "scene.h"
24
25 #include "osl_services.h"
26 #include "osl_shader.h"
27
28 #include "util_foreach.h"
29 #include "util_string.h"
30
31 #include "kernel_compat_cpu.h"
32 #include "kernel_globals.h"
33 #include "kernel_object.h"
34 #include "kernel_triangle.h"
35
36 CCL_NAMESPACE_BEGIN
37
38 /* RenderServices implementation */
39
40 #define TO_MATRIX44(m) (*(OSL::Matrix44 *)&(m))
41
42 /* static ustrings */
43 ustring OSLRenderServices::u_distance("distance");
44 ustring OSLRenderServices::u_index("index");
45 ustring OSLRenderServices::u_camera("camera");
46 ustring OSLRenderServices::u_screen("screen");
47 ustring OSLRenderServices::u_raster("raster");
48 ustring OSLRenderServices::u_ndc("NDC");
49 ustring OSLRenderServices::u_empty;
50
51 OSLRenderServices::OSLRenderServices()
52 {
53         kernel_globals = NULL;
54 }
55
56 OSLRenderServices::~OSLRenderServices()
57 {
58 }
59
60 void OSLRenderServices::thread_init(KernelGlobals *kernel_globals_)
61 {
62         kernel_globals = kernel_globals_;
63 }
64
65 bool OSLRenderServices::get_matrix(OSL::Matrix44 &result, OSL::TransformationPtr xform, float time)
66 {
67         /* this is only used for shader and object space, we don't really have
68          * a concept of shader space, so we just use object space for both. */
69         if (xform) {
70                 KernelGlobals *kg = kernel_globals;
71                 const ShaderData *sd = (const ShaderData *)xform;
72                 int object = sd->object;
73
74                 if (object != ~0) {
75                         Transform tfm = object_fetch_transform(kg, object, time, OBJECT_TRANSFORM);
76                         tfm = transform_transpose(tfm);
77                         result = TO_MATRIX44(tfm);
78
79                         return true;
80                 }
81         }
82
83         return false;
84 }
85
86 bool OSLRenderServices::get_inverse_matrix(OSL::Matrix44 &result, OSL::TransformationPtr xform, float time)
87 {
88         /* this is only used for shader and object space, we don't really have
89          * a concept of shader space, so we just use object space for both. */
90         if (xform) {
91                 KernelGlobals *kg = kernel_globals;
92                 const ShaderData *sd = (const ShaderData *)xform;
93                 int object = sd->object;
94
95                 if (object != ~0) {
96                         Transform tfm = object_fetch_transform(kg, object, time, OBJECT_INVERSE_TRANSFORM);
97                         tfm = transform_transpose(tfm);
98                         result = TO_MATRIX44(tfm);
99
100                         return true;
101                 }
102         }
103
104         return false;
105 }
106
107 bool OSLRenderServices::get_matrix(OSL::Matrix44 &result, ustring from, float time)
108 {
109         KernelGlobals *kg = kernel_globals;
110
111         if (from == u_ndc) {
112                 Transform tfm = transform_transpose(kernel_data.cam.ndctoworld);
113                 result = TO_MATRIX44(tfm);
114                 return true;
115         }
116         else if (from == u_raster) {
117                 Transform tfm = transform_transpose(kernel_data.cam.rastertoworld);
118                 result = TO_MATRIX44(tfm);
119                 return true;
120         }
121         else if (from == u_screen) {
122                 Transform tfm = transform_transpose(kernel_data.cam.screentoworld);
123                 result = TO_MATRIX44(tfm);
124                 return true;
125         }
126         else if (from == u_camera) {
127                 Transform tfm = transform_transpose(kernel_data.cam.cameratoworld);
128                 result = TO_MATRIX44(tfm);
129                 return true;
130         }
131
132         return false;
133 }
134
135 bool OSLRenderServices::get_inverse_matrix(OSL::Matrix44 &result, ustring to, float time)
136 {
137         KernelGlobals *kg = kernel_globals;
138
139         if (to == u_ndc) {
140                 Transform tfm = transform_transpose(kernel_data.cam.worldtondc);
141                 result = TO_MATRIX44(tfm);
142                 return true;
143         }
144         else if (to == u_raster) {
145                 Transform tfm = transform_transpose(kernel_data.cam.worldtoraster);
146                 result = TO_MATRIX44(tfm);
147                 return true;
148         }
149         else if (to == u_screen) {
150                 Transform tfm = transform_transpose(kernel_data.cam.worldtoscreen);
151                 result = TO_MATRIX44(tfm);
152                 return true;
153         }
154         else if (to == u_camera) {
155                 Transform tfm = transform_transpose(kernel_data.cam.worldtocamera);
156                 result = TO_MATRIX44(tfm);
157                 return true;
158         }
159
160         return false;
161 }
162
163 bool OSLRenderServices::get_array_attribute(void *renderstate, bool derivatives, 
164                                             ustring object, TypeDesc type, ustring name,
165                                             int index, void *val)
166 {
167         return false;
168 }
169
170 static bool get_mesh_attribute(KernelGlobals *kg, const ShaderData *sd,
171                                const OSLGlobals::Attribute& attr, bool derivatives, void *val)
172 {
173         if (attr.type == TypeDesc::TypeFloat) {
174                 float *fval = (float *)val;
175                 fval[0] = triangle_attribute_float(kg, sd, attr.elem, attr.offset,
176                                                    (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
177         }
178         else {
179                 /* todo: this won't work when float3 has w component */
180                 float3 *fval = (float3 *)val;
181                 fval[0] = triangle_attribute_float3(kg, sd, attr.elem, attr.offset,
182                                                     (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
183         }
184
185         return true;
186 }
187
188 static bool get_mesh_attribute_convert(KernelGlobals *kg, const ShaderData *sd,
189                                        const OSLGlobals::Attribute& attr, const TypeDesc& type, bool derivatives, void *val)
190 {
191         if (attr.type == TypeDesc::TypeFloat) {
192                 float tmp[3];
193                 float3 *fval = (float3 *)val;
194
195                 get_mesh_attribute(kg, sd, attr, derivatives, tmp);
196
197                 fval[0] = make_float3(tmp[0], tmp[0], tmp[0]);
198                 if (derivatives) {
199                         fval[1] = make_float3(tmp[1], tmp[1], tmp[1]);
200                         fval[2] = make_float3(tmp[2], tmp[2], tmp[2]);
201                 }
202
203                 return true;
204         }
205         else if (attr.type == TypeDesc::TypePoint || attr.type == TypeDesc::TypeVector ||
206                  attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor)
207         {
208                 float3 tmp[3];
209                 float *fval = (float *)val;
210
211                 get_mesh_attribute(kg, sd, attr, derivatives, tmp);
212
213                 fval[0] = average(tmp[0]);
214                 if (derivatives) {
215                         fval[1] = average(tmp[1]);
216                         fval[2] = average(tmp[2]);
217                 }
218
219                 return true;
220         }
221         else
222                 return false;
223 }
224
225 static void get_object_attribute(const OSLGlobals::Attribute& attr, bool derivatives, void *val)
226 {
227         size_t datasize = attr.value.datasize();
228
229         memcpy(val, attr.value.data(), datasize);
230         if (derivatives)
231                 memset((char *)val + datasize, 0, datasize * 2);
232 }
233
234 bool OSLRenderServices::get_attribute(void *renderstate, bool derivatives, ustring object_name,
235                                       TypeDesc type, ustring name, void *val)
236 {
237         KernelGlobals *kg = kernel_globals;
238         const ShaderData *sd = (const ShaderData *)renderstate;
239         int object = sd->object;
240         int tri = sd->prim;
241
242         /* lookup of attribute on another object */
243         if (object_name != u_empty) {
244                 OSLGlobals::ObjectNameMap::iterator it = kg->osl.object_name_map.find(object_name);
245
246                 if (it == kg->osl.object_name_map.end())
247                         return false;
248
249                 object = it->second;
250                 tri = ~0;
251         }
252         else if (object == ~0) {
253                 /* no background attributes supported */
254                 return false;
255         }
256
257         /* find attribute on object */
258         OSLGlobals::AttributeMap& attribute_map = kg->osl.attribute_map[object];
259         OSLGlobals::AttributeMap::iterator it = attribute_map.find(name);
260
261         if (it == attribute_map.end())
262                 return false;
263
264         /* type mistmatch? */
265         const OSLGlobals::Attribute& attr = it->second;
266
267         if (attr.elem != ATTR_ELEMENT_VALUE) {
268                 /* triangle and vertex attributes */
269                 if (tri != ~0) {
270                         if (attr.type == type || (attr.type == TypeDesc::TypeColor &&
271                                                   (type == TypeDesc::TypePoint || type == TypeDesc::TypeVector || type == TypeDesc::TypeNormal)))
272                         {
273                                 return get_mesh_attribute(kg, sd, attr, derivatives, val);
274                         }
275                         else {
276                                 return get_mesh_attribute_convert(kg, sd, attr, type, derivatives, val);
277                         }
278                 }
279         }
280         else {
281                 /* object attribute */
282                 get_object_attribute(attr, derivatives, val);
283                 return true;
284         }
285
286         return false;
287 }
288
289 bool OSLRenderServices::get_userdata(bool derivatives, ustring name, TypeDesc type, 
290                                      void *renderstate, void *val)
291 {
292         return false; /* disabled by lockgeom */
293 }
294
295 bool OSLRenderServices::has_userdata(ustring name, TypeDesc type, void *renderstate)
296 {
297         return false; /* never called by OSL */
298 }
299
300 void *OSLRenderServices::get_pointcloud_attr_query(ustring *attr_names,
301                                                    TypeDesc *attr_types, int nattrs)
302 {
303 #ifdef WITH_PARTIO
304         m_attr_queries.push_back(AttrQuery());
305         AttrQuery &query = m_attr_queries.back();
306
307         /* make space for what we need. the only reason to use
308          * std::vector is to skip the delete */
309         query.attr_names.resize(nattrs);
310         query.attr_partio_types.resize(nattrs);
311         /* capacity will keep the length of the smallest array passed
312          * to the query. Just to prevent buffer overruns */
313         query.capacity = -1;
314
315         for (int i = 0; i < nattrs; ++i) {
316                 query.attr_names[i] = attr_names[i];
317
318                 TypeDesc element_type = attr_types[i].elementtype();
319
320                 if (query.capacity < 0)
321                         query.capacity = attr_types[i].numelements();
322                 else
323                         query.capacity = min(query.capacity, (int)attr_types[i].numelements());
324
325                 /* convert the OSL (OIIO) type to the equivalent Partio type so
326                  * we can do a fast check at query time. */
327                 if (element_type == TypeDesc::TypeFloat) {
328                         query.attr_partio_types[i] = Partio::FLOAT;
329                 }
330                 else if (element_type == TypeDesc::TypeInt) {
331                         query.attr_partio_types[i] = Partio::INT;
332                 }
333                 else if (element_type == TypeDesc::TypeColor  || element_type == TypeDesc::TypePoint ||
334                          element_type == TypeDesc::TypeVector || element_type == TypeDesc::TypeNormal)
335                 {
336                         query.attr_partio_types[i] = Partio::VECTOR;
337                 }
338                 else {
339                         return NULL;  /* report some error of unknown type */
340                 }
341         }
342
343         /* this is valid until the end of RenderServices */
344         return &query;
345 #else
346         return NULL;
347 #endif
348 }
349
350 #ifdef WITH_PARTIO
351 Partio::ParticlesData *OSLRenderServices::get_pointcloud(ustring filename)
352 {
353         return Partio::readCached(filename.c_str(), true);
354 }
355
356 #endif
357
358 int OSLRenderServices::pointcloud(ustring filename, const OSL::Vec3 &center, float radius,
359                                   int max_points, void *_attr_query, void **attr_outdata)
360 {
361         /* todo: this code has never been tested, and most likely does not
362          * work. it's based on the example code in OSL */
363
364 #ifdef WITH_PARTIO
365         /* query Partio for this pointcloud lookup using cached attr_query */
366         if (!_attr_query)
367                 return 0;
368
369         AttrQuery *attr_query = (AttrQuery *)_attr_query;
370         if (attr_query->capacity < max_points)
371                 return 0;
372
373         /* get the pointcloud entry for the given filename */
374         Partio::ParticlesData *cloud = get_pointcloud(filename);
375
376         /* now we have to look up all the attributes in the file. we can't do this
377          * before hand cause we never know what we are going to load. */
378         int nattrs = attr_query->attr_names.size();
379         Partio::ParticleAttribute *attr = (Partio::ParticleAttribute *)alloca(sizeof(Partio::ParticleAttribute) * nattrs);
380
381         for (int i = 0; i < nattrs; ++i) {
382                 /* special case attributes */
383                 if (attr_query->attr_names[i] == u_distance || attr_query->attr_names[i] == u_index)
384                         continue;
385
386                 /* lookup the attribute by name*/
387                 if (!cloud->attributeInfo(attr_query->attr_names[i].c_str(), attr[i])) {
388                         /* issue an error here and return, types don't match */
389                         Partio::endCachedAccess(cloud);
390                         cloud->release();
391                         return 0;
392                 }
393         }
394
395         std::vector<Partio::ParticleIndex> indices;
396         std::vector<float> dist2;
397
398         Partio::beginCachedAccess(cloud);
399
400         /* finally, do the lookup */
401         cloud->findNPoints((const float *)&center, max_points, radius, indices, dist2);
402         int count = indices.size();
403
404         /* retrieve the attributes directly to user space */
405         for (int j = 0; j < nattrs; ++j) {
406                 /* special cases */
407                 if (attr_query->attr_names[j] == u_distance) {
408                         for (int i = 0; i < count; ++i)
409                                 ((float *)attr_outdata[j])[i] = sqrtf(dist2[i]);
410                 }
411                 else if (attr_query->attr_names[j] == u_index) {
412                         for (int i = 0; i < count; ++i)
413                                 ((int *)attr_outdata[j])[i] = indices[i];
414                 }
415                 else {
416                         /* note we make a single call per attribute, we don't loop over the
417                          * points. Partio does it, so it is there that we have to care about
418                          * performance */
419                         cloud->data(attr[j], count, &indices[0], true, attr_outdata[j]);
420                 }
421         }
422
423         Partio::endCachedAccess(cloud);
424         cloud->release();
425
426         return count;
427 #else
428         return 0;
429 #endif
430 }
431
432 CCL_NAMESPACE_END
433