// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include // Interpolation of a scalar-valued function defined on a sphere. Although // the sphere lives in 3D, the interpolation is a 2D method whose input // points are angles (theta,phi) from spherical coordinates. The domains of // the angles are -pi <= theta <= pi and 0 <= phi <= pi. namespace WwiseGTE { template class IntpSphere2 { public: // Construction and destruction. For complete spherical coverage, // include the two antipodal (theta,phi) points (-pi,0,F(-pi,0)) and // (-pi,pi,F(-pi,pi)) in the input data. These correspond to the // sphere poles x = 0, y = 0, and |z| = 1. ~IntpSphere2() = default; IntpSphere2(int numPoints, InputType const* theta, InputType const* phi, InputType const* F) : mMesh(mDelaunay) { // Copy the input data. The larger arrays are used to support // wrap-around in the Delaunay triangulation for the interpolator. int totalPoints = 3 * numPoints; mWrapAngles.resize(totalPoints); mWrapF.resize(totalPoints); for (int i = 0; i < numPoints; ++i) { mWrapAngles[i][0] = theta[i]; mWrapAngles[i][1] = phi[i]; mWrapF[i] = F[i]; } // Use periodicity to get wrap-around in the Delaunay // triangulation. int i0 = 0, i1 = numPoints, i2 = 2 * numPoints; for (/**/; i0 < numPoints; ++i0, ++i1, ++i2) { mWrapAngles[i1][0] = mWrapAngles[i0][0] + (InputType)GTE_C_TWO_PI; mWrapAngles[i2][0] = mWrapAngles[i0][0] - (InputType)GTE_C_TWO_PI; mWrapAngles[i1][1] = mWrapAngles[i0][1]; mWrapAngles[i2][1] = mWrapAngles[i0][1]; mWrapF[i1] = mWrapF[i0]; mWrapF[i2] = mWrapF[i0]; } mDelaunay(totalPoints, &mWrapAngles[0], (ComputeType)0); mInterp = std::make_unique>( mMesh, &mWrapF[0], (InputType)1); } // Spherical coordinates are // x = cos(theta)*sin(phi) // y = sin(theta)*sin(phi) // z = cos(phi) // for -pi <= theta <= pi, 0 <= phi <= pi. The application can use // this function to convert unit length vectors (x,y,z) to (theta,phi). static void GetSphericalCoordinates(InputType x, InputType y, InputType z, InputType& theta, InputType& phi) { // Assumes (x,y,z) is unit length. Returns -pi <= theta <= pi and // 0 <= phiAngle <= pi. if (z < (InputType)1) { if (z > -(InputType)1) { theta = std::atan2(y, x); phi = std::acos(z); } else { theta = -(InputType)GTE_C_PI; phi = (InputType)GTE_C_PI; } } else { theta = -(InputType)GTE_C_PI; phi = (InputType)0; } } // The return value is 'true' if and only if the input point is in the // convex hull of the input (theta,pi) array, in which case the // interpolation is valid. bool operator()(InputType theta, InputType phi, InputType& F) const { Vector2 angles{ theta, phi }; InputType thetaDeriv, phiDeriv; return (*mInterp)(angles, F, thetaDeriv, phiDeriv); } private: typedef Delaunay2Mesh TriangleMesh; std::vector> mWrapAngles; Delaunay2 mDelaunay; TriangleMesh mMesh; std::vector mWrapF; std::unique_ptr> mInterp; }; }