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 /////////////////////////////////////////////////////////////////////////
7 /// \file
8 ///
9 /// Shader interpreter implementation of matrix operations.
10 ///
11 /////////////////////////////////////////////////////////////////////////
12 
13 #include "oslexec_pvt.h"
14 #include <OSL/dual.h>
15 #include <OSL/dual_vec.h>
16 #include <OSL/device_string.h>
17 
18 #include <OpenImageIO/fmath.h>
19 #include <OpenImageIO/simd.h>
20 
21 #include <iostream>
22 #include <cmath>
23 
24 
25 
26 OSL_NAMESPACE_ENTER
27 namespace pvt {
28 
29 
30 
31 // Matrix ops
32 
33 OSL_SHADEOP OSL_HOSTDEVICE void
osl_mul_mmm(void * r,void * a,void * b)34 osl_mul_mmm (void *r, void *a, void *b)
35 {
36     MAT(r) = MAT(a) * MAT(b);
37 }
38 
39 OSL_SHADEOP OSL_HOSTDEVICE void
osl_mul_mmf(void * r,void * a,float b)40 osl_mul_mmf (void *r, void *a, float b)
41 {
42     MAT(r) = MAT(a) * b;
43 }
44 
45 
46 OSL_SHADEOP OSL_HOSTDEVICE void
osl_div_mmm(void * r,void * a,void * b)47 osl_div_mmm (void *r, void *a, void *b)
48 {
49     MAT(r) = MAT(a) * MAT(b).inverse();
50 }
51 
52 OSL_SHADEOP OSL_HOSTDEVICE void
osl_div_mmf(void * r,void * a,float b)53 osl_div_mmf (void *r, void *a, float b)
54 {
55     MAT(r) = MAT(a) * (1.0f/b);
56 }
57 
58 OSL_SHADEOP OSL_HOSTDEVICE void
osl_div_mfm(void * r,float a,void * b)59 osl_div_mfm (void *r, float a, void *b)
60 {
61     MAT(r) = a * MAT(b).inverse();
62 }
63 
64 OSL_SHADEOP OSL_HOSTDEVICE void
osl_div_m_ff(void * r,float a,float b)65 osl_div_m_ff (void *r, float a, float b)
66 {
67     float f = (b == 0) ? 0.0f : (a / b);
68     MAT(r) = Matrix44 (f,0,0,0, 0,f,0,0, 0,0,f,0, 0,0,0,f);
69 }
70 
71 
72 
73 OSL_SHADEOP OSL_HOSTDEVICE void
osl_transpose_mm(void * r,void * m)74 osl_transpose_mm (void *r, void *m)
75 {
76     //MAT(r) = MAT(m).transposed();
77 	MAT(r) = inlinedTransposed(MAT(m));
78 }
79 
80 
81 // point = M * point
osl_transform_vmv(void * result,void * M_,void * v_)82 OSL_SHADEOP OSL_HOSTDEVICE void osl_transform_vmv(void *result, void* M_, void* v_)
83 {
84    const Vec3 &v = VEC(v_);
85    const Matrix44 &M = MAT(M_);
86    robust_multVecMatrix (M, v, VEC(result));
87 }
88 
osl_transform_dvmdv(void * result,void * M_,void * v_)89 OSL_SHADEOP OSL_HOSTDEVICE void osl_transform_dvmdv(void *result, void* M_, void* v_)
90 {
91    const Dual2<Vec3> &v = DVEC(v_);
92    const Matrix44    &M = MAT(M_);
93    robust_multVecMatrix (M, v, DVEC(result));
94 }
95 
96 // vector = M * vector
osl_transformv_vmv(void * result,void * M_,void * v_)97 OSL_SHADEOP OSL_HOSTDEVICE void osl_transformv_vmv(void *result, void* M_, void* v_)
98 {
99    const Vec3 &v = VEC(v_);
100    const Matrix44 &M = MAT(M_);
101    //M.multDirMatrix (v, VEC(result));
102    multDirMatrix (M, v, VEC(result));
103 }
104 
osl_transformv_dvmdv(void * result,void * M_,void * v_)105 OSL_SHADEOP OSL_HOSTDEVICE void osl_transformv_dvmdv(void *result, void* M_, void* v_)
106 {
107    const Dual2<Vec3> &v = DVEC(v_);
108    const Matrix44    &M = MAT(M_);
109    multDirMatrix (M, v, DVEC(result));
110 }
111 
112 
113 // normal = M * normal
osl_transformn_vmv(void * result,void * M_,void * v_)114 OSL_SHADEOP OSL_HOSTDEVICE void osl_transformn_vmv(void *result, void* M_, void* v_)
115 {
116    const Vec3 &v = VEC(v_);
117    const Matrix44 &M = MAT(M_);
118    //M.inverse().transposed().multDirMatrix (v, VEC(result));
119    multDirMatrix(inlinedTransposed(M.inverse()), v, VEC(result));
120 }
121 
osl_transformn_dvmdv(void * result,void * M_,void * v_)122 OSL_SHADEOP OSL_HOSTDEVICE void osl_transformn_dvmdv(void *result, void* M_, void* v_)
123 {
124    const Dual2<Vec3> &v = DVEC(v_);
125    const Matrix44    &M = MAT(M_);
126    //multDirMatrix (M.inverse().transposed(), v, DVEC(result));
127    multDirMatrix (inlinedTransposed(M.inverse()), v, DVEC(result));
128 }
129 
130 #ifndef __CUDACC__
131 OSL_SHADEOP int
osl_get_matrix(void * sg_,void * r,const char * from)132 osl_get_matrix (void *sg_, void *r, const char *from)
133 {
134     ShaderGlobals *sg = (ShaderGlobals *)sg_;
135     ShadingContext *ctx = (ShadingContext *)sg->context;
136     if (USTR(from) == Strings::common ||
137             USTR(from) == ctx->shadingsys().commonspace_synonym()) {
138         MAT(r).makeIdentity ();
139         return true;
140     }
141     if (USTR(from) == Strings::shader) {
142         ctx->renderer()->get_matrix (sg, MAT(r), sg->shader2common, sg->time);
143         return true;
144     }
145     if (USTR(from) == Strings::object) {
146         ctx->renderer()->get_matrix (sg, MAT(r), sg->object2common, sg->time);
147         return true;
148     }
149     int ok = ctx->renderer()->get_matrix (sg, MAT(r), USTR(from), sg->time);
150     if (! ok) {
151         MAT(r).makeIdentity();
152         ShadingContext *ctx = (ShadingContext *)((ShaderGlobals *)sg)->context;
153         if (ctx->shadingsys().unknown_coordsys_error())
154             ctx->errorf("Unknown transformation \"%s\"", from);
155     }
156     return ok;
157 }
158 
159 
160 
161 OSL_SHADEOP int
osl_get_inverse_matrix(void * sg_,void * r,const char * to)162 osl_get_inverse_matrix (void *sg_, void *r, const char *to)
163 {
164     ShaderGlobals *sg = (ShaderGlobals *)sg_;
165     ShadingContext *ctx = (ShadingContext *)sg->context;
166     if (USTR(to) == Strings::common ||
167             USTR(to) == ctx->shadingsys().commonspace_synonym()) {
168         MAT(r).makeIdentity ();
169         return true;
170     }
171     if (USTR(to) == Strings::shader) {
172         ctx->renderer()->get_inverse_matrix (sg, MAT(r), sg->shader2common, sg->time);
173         return true;
174     }
175     if (USTR(to) == Strings::object) {
176         ctx->renderer()->get_inverse_matrix (sg, MAT(r), sg->object2common, sg->time);
177         return true;
178     }
179     int ok = ctx->renderer()->get_inverse_matrix (sg, MAT(r), USTR(to), sg->time);
180     if (! ok) {
181         MAT(r).makeIdentity ();
182         ShadingContext *ctx = (ShadingContext *)((ShaderGlobals *)sg)->context;
183         if (ctx->shadingsys().unknown_coordsys_error())
184             ctx->errorf("Unknown transformation \"%s\"", to);
185     }
186     return ok;
187 }
188 #else
189 // Implemented by the renderer
190 #define OSL_SHADEOP_EXPORT extern "C" OSL_DLL_EXPORT
191 OSL_SHADEOP_EXPORT OSL_HOSTDEVICE int osl_get_matrix (void *sg_, void *r, const char *from);
192 OSL_SHADEOP_EXPORT OSL_HOSTDEVICE int osl_get_inverse_matrix (void *sg_, void *r, const char *to);
193 #undef OSL_SHADEOP_EXPORT
194 #endif // __CUDACC__
195 
196 
197 
198 OSL_SHADEOP OSL_HOSTDEVICE int
osl_prepend_matrix_from(void * sg,void * r,const char * from)199 osl_prepend_matrix_from (void *sg, void *r, const char *from)
200 {
201     Matrix44 m;
202     bool ok = osl_get_matrix ((ShaderGlobals *)sg, &m, from);
203     if (ok)
204         MAT(r) = m * MAT(r);
205 #ifndef __CUDACC__
206     // TODO: How do we manage this in OptiX?
207     else {
208         ShadingContext *ctx = (ShadingContext *)((ShaderGlobals *)sg)->context;
209         if (ctx->shadingsys().unknown_coordsys_error())
210             ctx->errorf("Unknown transformation \"%s\"", from);
211     }
212 #endif
213     return ok;
214 }
215 
216 
217 
218 OSL_SHADEOP OSL_HOSTDEVICE int
osl_get_from_to_matrix(void * sg,void * r,const char * from,const char * to)219 osl_get_from_to_matrix (void *sg, void *r, const char *from, const char *to)
220 {
221     Matrix44 Mfrom, Mto;
222     int ok = osl_get_matrix ((ShaderGlobals *)sg, &Mfrom, from);
223     ok &= osl_get_inverse_matrix ((ShaderGlobals *)sg, &Mto, to);
224     MAT(r) = Mfrom * Mto;
225     return ok;
226 }
227 
228 
229 
230 OSL_SHADEOP OSL_HOSTDEVICE int
osl_transform_triple(void * sg_,void * Pin,int Pin_derivs,void * Pout,int Pout_derivs,void * from,void * to,int vectype)231 osl_transform_triple (void *sg_, void *Pin, int Pin_derivs,
232                       void *Pout, int Pout_derivs,
233                       void *from, void *to, int vectype)
234 {
235     ShaderGlobals *sg = (ShaderGlobals *)sg_;
236     Matrix44 M;
237     int ok;
238     Pin_derivs &= Pout_derivs;   // ignore derivs if output doesn't need it
239     if (HDSTR(from) == StringParams::common)
240         ok = osl_get_inverse_matrix (sg, &M, (const char *)to);
241     else if (HDSTR(to) == StringParams::common)
242         ok = osl_get_matrix (sg, &M, (const char *)from);
243     else
244         ok = osl_get_from_to_matrix (sg, &M, (const char *)from,
245                                      (const char *)to);
246     if (ok) {
247         if (vectype == TypeDesc::POINT) {
248             if (Pin_derivs)
249                 osl_transform_dvmdv(Pout, &M, Pin);
250             else
251                 osl_transform_vmv(Pout, &M, Pin);
252         } else if (vectype == TypeDesc::VECTOR) {
253             if (Pin_derivs)
254                 osl_transformv_dvmdv(Pout, &M, Pin);
255             else
256                 osl_transformv_vmv(Pout, &M, Pin);
257         } else if (vectype == TypeDesc::NORMAL) {
258             if (Pin_derivs)
259                 osl_transformn_dvmdv(Pout, &M, Pin);
260             else
261                 osl_transformn_vmv(Pout, &M, Pin);
262         }
263 #ifndef __CUDACC__
264         else OSL_DASSERT(0 && "Unknown transform type");
265 #else
266         // TBR: Is the ok?
267         else ok = false;
268 #endif
269     } else {
270         *(Vec3 *)Pout = *(Vec3 *)Pin;
271         if (Pin_derivs) {
272             ((Vec3 *)Pout)[1] = ((Vec3 *)Pin)[1];
273             ((Vec3 *)Pout)[2] = ((Vec3 *)Pin)[2];
274         }
275     }
276     if (Pout_derivs && !Pin_derivs) {
277         ((Vec3 *)Pout)[1].setValue (0.0f, 0.0f, 0.0f);
278         ((Vec3 *)Pout)[2].setValue (0.0f, 0.0f, 0.0f);
279     }
280     return ok;
281 }
282 
283 
284 
285 OSL_SHADEOP OSL_HOSTDEVICE int
osl_transform_triple_nonlinear(void * sg_,void * Pin,int Pin_derivs,void * Pout,int Pout_derivs,void * from,void * to,int vectype)286 osl_transform_triple_nonlinear (void *sg_, void *Pin, int Pin_derivs,
287                                 void *Pout, int Pout_derivs,
288                                 void *from, void *to,
289                                 int vectype)
290 {
291     ShaderGlobals *sg = (ShaderGlobals *)sg_;
292 #ifndef __CUDACC__
293     RendererServices *rend = sg->renderer;
294     if (rend->transform_points (sg, USTR(from), USTR(to), sg->time,
295                                 (const Vec3 *)Pin, (Vec3 *)Pout, 1,
296                                 (TypeDesc::VECSEMANTICS)vectype)) {
297         // Renderer had a direct way to transform the points between the
298         // two spaces.
299         if (Pout_derivs) {
300             if (Pin_derivs) {
301                 rend->transform_points (sg, USTR(from), USTR(to), sg->time,
302                                         (const Vec3 *)Pin+1,
303                                         (Vec3 *)Pout+1, 2, TypeDesc::VECTOR);
304             } else {
305                 ((Vec3 *)Pout)[1].setValue (0.0f, 0.0f, 0.0f);
306                 ((Vec3 *)Pout)[2].setValue (0.0f, 0.0f, 0.0f);
307             }
308         }
309         return true;
310     }
311 #endif // __CUDACC__
312 
313     // Renderer couldn't or wouldn't transform directly
314     // Except in OptiX we're the renderer will directly implement
315     // the transform in osl_transform_triple.
316     return osl_transform_triple (sg, Pin, Pin_derivs, Pout, Pout_derivs,
317                                  from, to, vectype);
318 }
319 
320 
321 // Calculate the determinant of a 2x2 matrix.
322 template <typename F>
det2x2(F a,F b,F c,F d)323 OSL_HOSTDEVICE inline F det2x2(F a, F b, F c, F d)
324 {
325     return a * d - b * c;
326 }
327 
328 // calculate the determinant of a 3x3 matrix in the form:
329 //     | a1,  b1,  c1 |
330 //     | a2,  b2,  c2 |
331 //     | a3,  b3,  c3 |
332 template <typename F>
det3x3(F a1,F a2,F a3,F b1,F b2,F b3,F c1,F c2,F c3)333 OSL_HOSTDEVICE inline F det3x3(F a1, F a2, F a3, F b1, F b2, F b3, F c1, F c2, F c3)
334 {
335     return a1 * det2x2( b2, b3, c2, c3 )
336          - b1 * det2x2( a2, a3, c2, c3 )
337          + c1 * det2x2( a2, a3, b2, b3 );
338 }
339 
340 // calculate the determinant of a 4x4 matrix.
341 template <typename F>
det4x4(const Imath::Matrix44<F> & m)342 OSL_HOSTDEVICE inline F det4x4(const Imath::Matrix44<F> &m)
343 {
344     // assign to individual variable names to aid selecting correct elements
345     F a1 = m.x[0][0], b1 = m.x[0][1], c1 = m.x[0][2], d1 = m.x[0][3];
346     F a2 = m.x[1][0], b2 = m.x[1][1], c2 = m.x[1][2], d2 = m.x[1][3];
347     F a3 = m.x[2][0], b3 = m.x[2][1], c3 = m.x[2][2], d3 = m.x[2][3];
348     F a4 = m.x[3][0], b4 = m.x[3][1], c4 = m.x[3][2], d4 = m.x[3][3];
349     return a1 * det3x3( b2, b3, b4, c2, c3, c4, d2, d3, d4)
350          - b1 * det3x3( a2, a3, a4, c2, c3, c4, d2, d3, d4)
351          + c1 * det3x3( a2, a3, a4, b2, b3, b4, d2, d3, d4)
352          - d1 * det3x3( a2, a3, a4, b2, b3, b4, c2, c3, c4);
353 }
354 
355 OSL_SHADEOP OSL_HOSTDEVICE float
osl_determinant_fm(void * m)356 osl_determinant_fm (void *m)
357 {
358     return det4x4 (MAT(m));
359 }
360 
361 
362 
363 
364 } // namespace pvt
365 OSL_NAMESPACE_EXIT
366