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