1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 
6 #include <OpenImageIO/imagebufalgo.h>
7 
8 #include <OSL/oslexec.h>
9 #include <OSL/genclosure.h>
10 #include "simplerend.h"
11 using namespace OSL;
12 
13 
14 // anonymous namespace
15 namespace {
16 
17 // unique identifier for each closure supported by testshade
18 enum ClosureIDs {
19     EMISSION_ID = 1,
20     BACKGROUND_ID,
21     DIFFUSE_ID,
22     OREN_NAYAR_ID,
23     TRANSLUCENT_ID,
24     PHONG_ID,
25     WARD_ID,
26     MICROFACET_ID,
27     REFLECTION_ID,
28     FRESNEL_REFLECTION_ID,
29     REFRACTION_ID,
30     TRANSPARENT_ID,
31     DEBUG_ID,
32     HOLDOUT_ID,
33 };
34 
35 // these structures hold the parameters of each closure type
36 // they will be contained inside ClosureComponent
37 struct EmptyParams      { };
38 struct DiffuseParams    { Vec3 N; ustring label; };
39 struct OrenNayarParams  { Vec3 N; float sigma; };
40 struct PhongParams      { Vec3 N; float exponent; ustring label; };
41 struct WardParams       { Vec3 N, T; float ax, ay; };
42 struct ReflectionParams { Vec3 N; float eta; };
43 struct RefractionParams { Vec3 N; float eta; };
44 struct MicrofacetParams { ustring dist; Vec3 N, U; float xalpha, yalpha, eta; int refract; };
45 struct DebugParams      { ustring tag; };
46 
47 } // anonymous namespace
48 
49 
50 OSL_NAMESPACE_ENTER
51 
52 static ustring u_camera("camera"), u_screen("screen");
53 static ustring u_NDC("NDC"), u_raster("raster");
54 static ustring u_perspective("perspective");
55 static ustring u_s("s"), u_t("t");
56 static TypeDesc TypeFloatArray2 (TypeDesc::FLOAT, 2);
57 static TypeDesc TypeFloatArray4 (TypeDesc::FLOAT, 4);
58 static TypeDesc TypeIntArray2 (TypeDesc::INT, 2);
59 
60 
register_closures(OSL::ShadingSystem * shadingsys)61 void register_closures(OSL::ShadingSystem* shadingsys) {
62     // Describe the memory layout of each closure type to the OSL runtime
63     enum { MaxParams = 32 };
64     struct BuiltinClosures {
65         const char* name;
66         int id;
67         ClosureParam params[MaxParams]; // upper bound
68     };
69     BuiltinClosures builtins[] = {
70         { "emission"   , EMISSION_ID,           { CLOSURE_FINISH_PARAM(EmptyParams) } },
71         { "background" , BACKGROUND_ID,         { CLOSURE_FINISH_PARAM(EmptyParams) } },
72         { "diffuse"    , DIFFUSE_ID,            { CLOSURE_VECTOR_PARAM(DiffuseParams, N),
73                                                   CLOSURE_STRING_KEYPARAM(DiffuseParams, label, "label"), // example of custom key param
74                                                   CLOSURE_FINISH_PARAM(DiffuseParams) } },
75         { "oren_nayar" , OREN_NAYAR_ID,         { CLOSURE_VECTOR_PARAM(OrenNayarParams, N),
76                                                   CLOSURE_FLOAT_PARAM (OrenNayarParams, sigma),
77                                                   CLOSURE_FINISH_PARAM(OrenNayarParams) } },
78         { "translucent", TRANSLUCENT_ID,        { CLOSURE_VECTOR_PARAM(DiffuseParams, N),
79                                                   CLOSURE_FINISH_PARAM(DiffuseParams) } },
80         { "phong"      , PHONG_ID,              { CLOSURE_VECTOR_PARAM(PhongParams, N),
81                                                   CLOSURE_FLOAT_PARAM (PhongParams, exponent),
82                                                   CLOSURE_STRING_KEYPARAM(PhongParams, label, "label"), // example of custom key param
83                                                   CLOSURE_FINISH_PARAM(PhongParams) } },
84         { "ward"       , WARD_ID,               { CLOSURE_VECTOR_PARAM(WardParams, N),
85                                                   CLOSURE_VECTOR_PARAM(WardParams, T),
86                                                   CLOSURE_FLOAT_PARAM (WardParams, ax),
87                                                   CLOSURE_FLOAT_PARAM (WardParams, ay),
88                                                   CLOSURE_FINISH_PARAM(WardParams) } },
89         { "microfacet", MICROFACET_ID,          { CLOSURE_STRING_PARAM(MicrofacetParams, dist),
90                                                   CLOSURE_VECTOR_PARAM(MicrofacetParams, N),
91                                                   CLOSURE_VECTOR_PARAM(MicrofacetParams, U),
92                                                   CLOSURE_FLOAT_PARAM (MicrofacetParams, xalpha),
93                                                   CLOSURE_FLOAT_PARAM (MicrofacetParams, yalpha),
94                                                   CLOSURE_FLOAT_PARAM (MicrofacetParams, eta),
95                                                   CLOSURE_INT_PARAM   (MicrofacetParams, refract),
96                                                   CLOSURE_FINISH_PARAM(MicrofacetParams) } },
97         { "reflection" , REFLECTION_ID,         { CLOSURE_VECTOR_PARAM(ReflectionParams, N),
98                                                   CLOSURE_FINISH_PARAM(ReflectionParams) } },
99         { "reflection" , FRESNEL_REFLECTION_ID, { CLOSURE_VECTOR_PARAM(ReflectionParams, N),
100                                                   CLOSURE_FLOAT_PARAM (ReflectionParams, eta),
101                                                   CLOSURE_FINISH_PARAM(ReflectionParams) } },
102         { "refraction" , REFRACTION_ID,         { CLOSURE_VECTOR_PARAM(RefractionParams, N),
103                                                   CLOSURE_FLOAT_PARAM (RefractionParams, eta),
104                                                   CLOSURE_FINISH_PARAM(RefractionParams) } },
105         { "transparent", TRANSPARENT_ID,        { CLOSURE_FINISH_PARAM(EmptyParams) } },
106         { "debug"      , DEBUG_ID,              { CLOSURE_STRING_PARAM(DebugParams, tag),
107                                                   CLOSURE_FINISH_PARAM(DebugParams) } },
108         { "holdout"    , HOLDOUT_ID,            { CLOSURE_FINISH_PARAM(EmptyParams) } }
109     };
110 
111     for (const auto& b : builtins)
112         shadingsys->register_closure(b.name, b.id, b.params, nullptr, nullptr);
113 }
114 
115 
116 
SimpleRenderer()117 SimpleRenderer::SimpleRenderer ()
118 {
119     Matrix44 M;  M.makeIdentity();
120     camera_params (M, u_perspective, 90.0f,
121                    0.1f, 1000.0f, 256, 256);
122 
123     // Set up getters
124     m_attr_getters[ustring("osl:version")] = &SimpleRenderer::get_osl_version;
125     m_attr_getters[ustring("camera:resolution")] = &SimpleRenderer::get_camera_resolution;
126     m_attr_getters[ustring("camera:projection")] = &SimpleRenderer::get_camera_projection;
127     m_attr_getters[ustring("camera:pixelaspect")] = &SimpleRenderer::get_camera_pixelaspect;
128     m_attr_getters[ustring("camera:screen_window")] = &SimpleRenderer::get_camera_screen_window;
129     m_attr_getters[ustring("camera:fov")] = &SimpleRenderer::get_camera_fov;
130     m_attr_getters[ustring("camera:clip")] = &SimpleRenderer::get_camera_clip;
131     m_attr_getters[ustring("camera:clip_near")] = &SimpleRenderer::get_camera_clip_near;
132     m_attr_getters[ustring("camera:clip_far")] = &SimpleRenderer::get_camera_clip_far;
133     m_attr_getters[ustring("camera:shutter")] = &SimpleRenderer::get_camera_shutter;
134     m_attr_getters[ustring("camera:shutter_open")] = &SimpleRenderer::get_camera_shutter_open;
135     m_attr_getters[ustring("camera:shutter_close")] = &SimpleRenderer::get_camera_shutter_close;
136 }
137 
138 
139 
140 int
supports(string_view) const141 SimpleRenderer::supports (string_view /*feature*/) const
142 {
143     return false;
144 }
145 
146 
147 
148 OIIO::ParamValue*
find_attribute(string_view name,TypeDesc searchtype,bool casesensitive)149 SimpleRenderer::find_attribute(string_view name, TypeDesc searchtype,
150                                bool casesensitive)
151 {
152     auto iter = options.find(name, searchtype, casesensitive);
153     if (iter != options.end())
154         return &(*iter);
155     return nullptr;
156 }
157 
158 
159 
160 const OIIO::ParamValue*
find_attribute(string_view name,TypeDesc searchtype,bool casesensitive) const161 SimpleRenderer::find_attribute(string_view name, TypeDesc searchtype,
162                                bool casesensitive) const
163 {
164     auto iter = options.find(name, searchtype, casesensitive);
165     if (iter != options.end())
166         return &(*iter);
167     return nullptr;
168 }
169 
170 
171 
172 void
attribute(string_view name,TypeDesc type,const void * value)173 SimpleRenderer::attribute (string_view name, TypeDesc type, const void *value)
174 {
175     if (name.empty())  // Guard against bogus empty names
176         return;
177     // Don't allow duplicates
178     auto f = find_attribute(name);
179     if (!f) {
180         options.resize(options.size() + 1);
181         f = &options.back();
182     }
183     f->init(name, type, 1, value);
184 }
185 
186 
187 
188 void
camera_params(const Matrix44 & world_to_camera,ustring projection,float hfov,float hither,float yon,int xres,int yres)189 SimpleRenderer::camera_params (const Matrix44 &world_to_camera,
190                                ustring projection, float hfov,
191                                float hither, float yon,
192                                int xres, int yres)
193 {
194     m_world_to_camera = world_to_camera;
195     m_projection = projection;
196     m_fov = hfov;
197     m_pixelaspect = 1.0f; // hard-coded
198     m_hither = hither;
199     m_yon = yon;
200     m_shutter[0] = 0.0f; m_shutter[1] = 1.0f;  // hard-coded
201     float frame_aspect = float(xres)/float(yres) * m_pixelaspect;
202     m_screen_window[0] = -frame_aspect;
203     m_screen_window[1] = -1.0f;
204     m_screen_window[2] =  frame_aspect;
205     m_screen_window[3] =  1.0f;
206     m_xres = xres;
207     m_yres = yres;
208 }
209 
210 
211 
212 bool
get_matrix(ShaderGlobals *,Matrix44 & result,TransformationPtr xform,float)213 SimpleRenderer::get_matrix (ShaderGlobals* /*sg*/, Matrix44 &result,
214                             TransformationPtr xform,
215                             float /*time*/)
216 {
217     // SimpleRenderer doesn't understand motion blur and transformations
218     // are just simple 4x4 matrices.
219     result = *reinterpret_cast<const Matrix44*>(xform);
220     return true;
221 }
222 
223 
224 
225 bool
get_matrix(ShaderGlobals *,Matrix44 & result,ustring from,float)226 SimpleRenderer::get_matrix (ShaderGlobals* /*sg*/, Matrix44 &result,
227                             ustring from, float /*time*/)
228 {
229     TransformMap::const_iterator found = m_named_xforms.find (from);
230     if (found != m_named_xforms.end()) {
231         result = *(found->second);
232         return true;
233     } else {
234         return false;
235     }
236 }
237 
238 
239 
240 bool
get_matrix(ShaderGlobals *,Matrix44 & result,TransformationPtr xform)241 SimpleRenderer::get_matrix (ShaderGlobals* /*sg*/, Matrix44 &result,
242                             TransformationPtr xform)
243 {
244     // SimpleRenderer doesn't understand motion blur and transformations
245     // are just simple 4x4 matrices.
246     result = *(OSL::Matrix44 *)xform;
247     return true;
248 }
249 
250 
251 
252 bool
get_matrix(ShaderGlobals *,Matrix44 & result,ustring from)253 SimpleRenderer::get_matrix (ShaderGlobals* /*sg*/, Matrix44 &result,
254                             ustring from)
255 {
256     // SimpleRenderer doesn't understand motion blur, so we never fail
257     // on account of time-varying transformations.
258     TransformMap::const_iterator found = m_named_xforms.find (from);
259     if (found != m_named_xforms.end()) {
260         result = *(found->second);
261         return true;
262     } else {
263         return false;
264     }
265 }
266 
267 
268 
269 bool
get_inverse_matrix(ShaderGlobals *,Matrix44 & result,ustring to,float)270 SimpleRenderer::get_inverse_matrix (ShaderGlobals* /*sg*/, Matrix44 &result,
271                                     ustring to, float /*time*/)
272 {
273     if (to == u_camera || to == u_screen || to == u_NDC || to == u_raster) {
274         Matrix44 M = m_world_to_camera;
275         if (to == u_screen || to == u_NDC || to == u_raster) {
276             float depthrange = (double)m_yon-(double)m_hither;
277             if (m_projection == u_perspective) {
278                 float tanhalffov = tanf (0.5f * m_fov * M_PI/180.0);
279                 Matrix44 camera_to_screen (1/tanhalffov, 0, 0, 0,
280                                            0, 1/tanhalffov, 0, 0,
281                                            0, 0, m_yon/depthrange, 1,
282                                            0, 0, -m_yon*m_hither/depthrange, 0);
283                 M = M * camera_to_screen;
284             } else {
285                 Matrix44 camera_to_screen (1, 0, 0, 0,
286                                            0, 1, 0, 0,
287                                            0, 0, 1/depthrange, 0,
288                                            0, 0, -m_hither/depthrange, 1);
289                 M = M * camera_to_screen;
290             }
291             if (to == u_NDC || to == u_raster) {
292                 float screenleft = -1.0, screenwidth = 2.0;
293                 float screenbottom = -1.0, screenheight = 2.0;
294                 Matrix44 screen_to_ndc (1/screenwidth, 0, 0, 0,
295                                         0, 1/screenheight, 0, 0,
296                                         0, 0, 1, 0,
297                                         -screenleft/screenwidth, -screenbottom/screenheight, 0, 1);
298                 M = M * screen_to_ndc;
299                 if (to == u_raster) {
300                     Matrix44 ndc_to_raster (m_xres, 0, 0, 0,
301                                             0, m_yres, 0, 0,
302                                             0, 0, 1, 0,
303                                             0, 0, 0, 1);
304                     M = M * ndc_to_raster;
305                 }
306             }
307         }
308         result = M;
309         return true;
310     }
311 
312     TransformMap::const_iterator found = m_named_xforms.find (to);
313     if (found != m_named_xforms.end()) {
314         result = *(found->second);
315         result.invert();
316         return true;
317     } else {
318         return false;
319     }
320 }
321 
322 
323 
324 void
name_transform(const char * name,const OSL::Matrix44 & xform)325 SimpleRenderer::name_transform (const char *name, const OSL::Matrix44 &xform)
326 {
327     std::shared_ptr<Transformation> M (new OSL::Matrix44 (xform));
328     m_named_xforms[ustring(name)] = M;
329 }
330 
331 
332 
333 bool
get_array_attribute(ShaderGlobals * sg,bool derivatives,ustring object,TypeDesc type,ustring name,int index,void * val)334 SimpleRenderer::get_array_attribute (ShaderGlobals *sg, bool derivatives, ustring object,
335                                      TypeDesc type, ustring name,
336                                      int index, void *val)
337 {
338     AttrGetterMap::const_iterator g = m_attr_getters.find (name);
339     if (g != m_attr_getters.end()) {
340         AttrGetter getter = g->second;
341         return (this->*(getter)) (sg, derivatives, object, type, name, val);
342     }
343 
344     // In order to test getattribute(), respond positively to
345     // "options"/"blahblah"
346     if (object == "options" && name == "blahblah" &&
347         type == TypeDesc::TypeFloat) {
348         *(float *)val = 3.14159;
349         return true;
350     }
351 
352     // If no named attribute was found, allow userdata to bind to the
353     // attribute request.
354     if (object.empty() && index == -1)
355         return get_userdata (derivatives, name, type, sg, val);
356 
357     return false;
358 }
359 
360 
361 
362 bool
get_attribute(ShaderGlobals * sg,bool derivatives,ustring object,TypeDesc type,ustring name,void * val)363 SimpleRenderer::get_attribute (ShaderGlobals *sg, bool derivatives, ustring object,
364                                TypeDesc type, ustring name, void *val)
365 {
366     return get_array_attribute (sg, derivatives, object,
367                                 type, name, -1, val);
368 }
369 
370 
371 
372 bool
get_userdata(bool derivatives,ustring name,TypeDesc type,ShaderGlobals * sg,void * val)373 SimpleRenderer::get_userdata (bool derivatives, ustring name, TypeDesc type,
374                               ShaderGlobals *sg, void *val)
375 {
376     // Just to illustrate how this works, respect s and t userdata, filled
377     // in with the uv coordinates.  In a real renderer, it would probably
378     // look up something specific to the primitive, rather than have hard-
379     // coded names.
380 
381     if (name == u_s && type == TypeDesc::TypeFloat) {
382         ((float *)val)[0] = sg->u;
383         if (derivatives) {
384             ((float *)val)[1] = sg->dudx;
385             ((float *)val)[2] = sg->dudy;
386         }
387         return true;
388     }
389     if (name == u_t && type == TypeDesc::TypeFloat) {
390         ((float *)val)[0] = sg->v;
391         if (derivatives) {
392             ((float *)val)[1] = sg->dvdx;
393             ((float *)val)[2] = sg->dvdy;
394         }
395         return true;
396     }
397 
398 #if OIIO_VERSION >= 20202
399     if (const OIIO::ParamValue* p = userdata.find_pv(name, type)) {
400         size_t size = p->type().size();
401         memcpy(val, p->data(), size);
402         if (derivatives)
403             memcpy(val, (char*)p->data() + size, 2 * size);
404         return true;
405     }
406 #else
407     auto p = userdata.find(name, type);
408     if (p != userdata.end()) {
409         size_t size = p->type().size();
410         memcpy(val, p->data(), size);
411         if (derivatives)
412             memcpy(val, (char*)p->data() + size, 2 * size);
413         return true;
414     }
415 #endif
416 
417     return false;
418 }
419 
420 
421 bool
get_osl_version(ShaderGlobals *,bool,ustring,TypeDesc type,ustring,void * val)422 SimpleRenderer::get_osl_version (ShaderGlobals* /*sg*/, bool /*derivs*/, ustring /*object*/,
423                                  TypeDesc type, ustring /*name*/, void *val)
424 {
425     if (type == TypeDesc::TypeInt) {
426         ((int *)val)[0] = OSL_VERSION;
427         return true;
428     }
429     return false;
430 }
431 
432 
433 bool
get_camera_resolution(ShaderGlobals *,bool,ustring,TypeDesc type,ustring,void * val)434 SimpleRenderer::get_camera_resolution (ShaderGlobals* /*sg*/, bool /*derivs*/, ustring /*object*/,
435                                     TypeDesc type, ustring /*name*/, void *val)
436 {
437     if (type == TypeIntArray2) {
438         ((int *)val)[0] = m_xres;
439         ((int *)val)[1] = m_yres;
440         return true;
441     }
442     return false;
443 }
444 
445 
446 bool
get_camera_projection(ShaderGlobals *,bool,ustring,TypeDesc type,ustring,void * val)447 SimpleRenderer::get_camera_projection (ShaderGlobals* /*sg*/, bool /*derivs*/, ustring /*object*/,
448                                     TypeDesc type, ustring /*name*/, void *val)
449 {
450     if (type == TypeDesc::TypeString) {
451         ((ustring *)val)[0] = m_projection;
452         return true;
453     }
454     return false;
455 }
456 
457 
458 bool
get_camera_fov(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)459 SimpleRenderer::get_camera_fov (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
460                                 TypeDesc type, ustring /*name*/, void *val)
461 {
462     // N.B. in a real renderer, this may be time-dependent
463     if (type == TypeDesc::TypeFloat) {
464         ((float *)val)[0] = m_fov;
465         if (derivs)
466             memset ((char *)val+type.size(), 0, 2*type.size());
467         return true;
468     }
469     return false;
470 }
471 
472 
473 bool
get_camera_pixelaspect(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)474 SimpleRenderer::get_camera_pixelaspect (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
475                                     TypeDesc type, ustring /*name*/, void *val)
476 {
477     if (type == TypeDesc::TypeFloat) {
478         ((float *)val)[0] = m_pixelaspect;
479         if (derivs)
480             memset ((char *)val+type.size(), 0, 2*type.size());
481         return true;
482     }
483     return false;
484 }
485 
486 
487 bool
get_camera_clip(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)488 SimpleRenderer::get_camera_clip (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
489                                     TypeDesc type, ustring /*name*/, void *val)
490 {
491     if (type == TypeFloatArray2) {
492         ((float *)val)[0] = m_hither;
493         ((float *)val)[1] = m_yon;
494         if (derivs)
495             memset ((char *)val+type.size(), 0, 2*type.size());
496         return true;
497     }
498     return false;
499 }
500 
501 
502 bool
get_camera_clip_near(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)503 SimpleRenderer::get_camera_clip_near (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
504                                     TypeDesc type, ustring /*name*/, void *val)
505 {
506     if (type == TypeDesc::TypeFloat) {
507         ((float *)val)[0] = m_hither;
508         if (derivs)
509             memset ((char *)val+type.size(), 0, 2*type.size());
510         return true;
511     }
512     return false;
513 }
514 
515 
516 bool
get_camera_clip_far(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)517 SimpleRenderer::get_camera_clip_far (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
518                                     TypeDesc type, ustring /*name*/, void *val)
519 {
520     if (type == TypeDesc::TypeFloat) {
521         ((float *)val)[0] = m_yon;
522         if (derivs)
523             memset ((char *)val+type.size(), 0, 2*type.size());
524         return true;
525     }
526     return false;
527 }
528 
529 
530 
531 bool
get_camera_shutter(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)532 SimpleRenderer::get_camera_shutter (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
533                                     TypeDesc type, ustring /*name*/, void *val)
534 {
535     if (type == TypeFloatArray2) {
536         ((float *)val)[0] = m_shutter[0];
537         ((float *)val)[1] = m_shutter[1];
538         if (derivs)
539             memset ((char *)val+type.size(), 0, 2*type.size());
540         return true;
541     }
542     return false;
543 }
544 
545 
546 bool
get_camera_shutter_open(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)547 SimpleRenderer::get_camera_shutter_open (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
548                                     TypeDesc type, ustring /*name*/, void *val)
549 {
550     if (type == TypeDesc::TypeFloat) {
551         ((float *)val)[0] = m_shutter[0];
552         if (derivs)
553             memset ((char *)val+type.size(), 0, 2*type.size());
554         return true;
555     }
556     return false;
557 }
558 
559 
560 bool
get_camera_shutter_close(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)561 SimpleRenderer::get_camera_shutter_close (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
562                                     TypeDesc type, ustring /*name*/, void *val)
563 {
564     if (type == TypeDesc::TypeFloat) {
565         ((float *)val)[0] = m_shutter[1];
566         if (derivs)
567             memset ((char *)val+type.size(), 0, 2*type.size());
568         return true;
569     }
570     return false;
571 }
572 
573 
574 bool
get_camera_screen_window(ShaderGlobals *,bool derivs,ustring,TypeDesc type,ustring,void * val)575 SimpleRenderer::get_camera_screen_window (ShaderGlobals* /*sg*/, bool derivs, ustring /*object*/,
576                                     TypeDesc type, ustring /*name*/, void *val)
577 {
578     // N.B. in a real renderer, this may be time-dependent
579     if (type == TypeFloatArray4) {
580         ((float *)val)[0] = m_screen_window[0];
581         ((float *)val)[1] = m_screen_window[1];
582         ((float *)val)[2] = m_screen_window[2];
583         ((float *)val)[3] = m_screen_window[3];
584         if (derivs)
585             memset ((char *)val+type.size(), 0, 2*type.size());
586         return true;
587     }
588     return false;
589 }
590 
591 
592 
593 bool
add_output(string_view varname,string_view filename,TypeDesc datatype,int nchannels)594 SimpleRenderer::add_output (string_view varname, string_view filename,
595                             TypeDesc datatype, int nchannels)
596 {
597     // FIXME: use name to figure out
598     OIIO::ImageSpec spec(m_xres, m_yres, nchannels, datatype);
599     m_outputvars.emplace_back(varname);
600     m_outputbufs.emplace_back(new OIIO::ImageBuf(filename, spec));
601     OIIO::ImageBufAlgo::zero (*m_outputbufs.back());
602     return true;
603 }
604 
605 
606 
607 OSL_NAMESPACE_EXIT
608