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