/// Implementation derived from Wild Magic (Version 2) Software Library, available /// from http://www.geometrictools.com/Downloads/WildMagic2p5.zip under free license int SpherePrimitiveEvaluator::intersectionPoints( const Imath::V3f &origin, const Imath::V3f &direction, std::vector<PrimitiveEvaluator::ResultPtr> &results, float maxDistance ) const { results.clear(); Imath::V3f dir = direction.normalized(); (void)direction; float a0 = origin.dot(origin) - m_sphere->radius() * m_sphere->radius(); float a1 = dir.dot( origin ); float discr = a1 * a1 - a0; if (discr < 0.0) { return 0; } if ( discr >= Imath::limits<float>::epsilon() ) { float root = sqrt( discr ); float t0 = -a1 - root; if ( t0 >= 0.0 ) { Imath::V3f p0 = origin + t0 * dir; if ( (origin - p0).length() < maxDistance ) { ResultPtr r = staticPointerCast< Result > ( createResult() ); r->m_p = p0; results.push_back( r ); } } float t1 = -a1 + root; if ( t1 >= 0.0 ) { Imath::V3f p1 = origin + t1 * dir; if ( (origin - p1).length() < maxDistance ) { ResultPtr r = staticPointerCast< Result > ( createResult() ); r->m_p = p1; results.push_back( r ); } } } else { float t = -a1; if ( t >= 0.0 ) { Imath::V3f p = origin + t * dir; if ( (origin - p).length() < maxDistance ) { ResultPtr r = staticPointerCast< Result > ( createResult() ); r->m_p = p; results.push_back( r ); } } } assert( results.size() >= 0 ); assert( results.size() <= 2 ); return results.size(); }
bool TextureSystemImpl::environment (TextureHandle *texture_handle_, Perthread *thread_info_, TextureOpt &options, const Imath::V3f &_R, const Imath::V3f &_dRdx, const Imath::V3f &_dRdy, float *result) { PerThreadInfo *thread_info = (PerThreadInfo *)thread_info_; TextureFile *texturefile = (TextureFile *)texture_handle_; ImageCacheStatistics &stats (thread_info->m_stats); ++stats.environment_batches; ++stats.environment_queries; if (! texturefile || texturefile->broken()) return missing_texture (options, result); const ImageSpec &spec (texturefile->spec(options.subimage, 0)); options.swrap_func = texturefile->m_sample_border ? wrap_periodic_sharedborder : wrap_periodic; options.twrap_func = wrap_clamp; options.envlayout = LayoutLatLong; int actualchannels = Imath::clamp (spec.nchannels - options.firstchannel, 0, options.nchannels); options.actualchannels = actualchannels; // Initialize results to 0. We'll add from here on as we sample. float* dresultds = options.dresultds; float* dresultdt = options.dresultdt; for (int c = 0; c < options.actualchannels; ++c) { result[c] = 0; if (dresultds) dresultds[c] = 0; if (dresultdt) dresultdt[c] = 0; } // If the user only provided us with one pointer, clear both to simplify // the rest of the code, but only after we zero out the data for them so // they know something went wrong. if (!(dresultds && dresultdt)) dresultds = dresultdt = NULL; // Calculate unit-length vectors in the direction of R, R+dRdx, R+dRdy. // These define the ellipse we're filtering over. Imath::V3f R = _R; R.normalize(); // center Imath::V3f Rx = _R + _dRdx; Rx.normalize(); // x axis of the ellipse Imath::V3f Ry = _R + _dRdy; Ry.normalize(); // y axis of the ellipse // angles formed by the ellipse axes. float xfilt_noblur = std::max (safe_acosf(R.dot(Rx)), 1e-8f); float yfilt_noblur = std::max (safe_acosf(R.dot(Ry)), 1e-8f); int naturalres = int((float)M_PI / std::min (xfilt_noblur, yfilt_noblur)); // FIXME -- figure naturalres sepearately for s and t // FIXME -- ick, why is it x and y at all, shouldn't it be s and t? // N.B. naturalres formulated for latlong // Account for width and blur float xfilt = xfilt_noblur * options.swidth + options.sblur; float yfilt = yfilt_noblur * options.twidth + options.tblur; // Figure out major versus minor, and aspect ratio Imath::V3f Rmajor; // major axis float majorlength, minorlength; bool x_is_majoraxis = (xfilt >= yfilt); if (x_is_majoraxis) { Rmajor = Rx; majorlength = xfilt; minorlength = yfilt; } else { Rmajor = Ry; majorlength = yfilt; minorlength = xfilt; } accum_prototype accumer; long long *probecount; switch (options.interpmode) { case TextureOpt::InterpClosest : accumer = &TextureSystemImpl::accum_sample_closest; probecount = &stats.closest_interps; break; case TextureOpt::InterpBilinear : accumer = &TextureSystemImpl::accum_sample_bilinear; probecount = &stats.bilinear_interps; break; case TextureOpt::InterpBicubic : accumer = &TextureSystemImpl::accum_sample_bicubic; probecount = &stats.cubic_interps; break; default: accumer = NULL; probecount = NULL; break; } TextureOpt::MipMode mipmode = options.mipmode; bool aniso = (mipmode == TextureOpt::MipModeDefault || mipmode == TextureOpt::MipModeAniso); float aspect, trueaspect, filtwidth; int nsamples; float invsamples; if (aniso) { aspect = anisotropic_aspect (majorlength, minorlength, options, trueaspect); filtwidth = minorlength; if (trueaspect > stats.max_aniso) stats.max_aniso = trueaspect; nsamples = std::max (1, (int) ceilf (aspect - 0.25f)); invsamples = 1.0f / nsamples; } else { filtwidth = options.conservative_filter ? majorlength : minorlength; nsamples = 1; invsamples = 1.0f; } ImageCacheFile::SubimageInfo &subinfo (texturefile->subimageinfo(options.subimage)); // FIXME -- assuming latlong bool ok = true; float pos = -0.5f + 0.5f * invsamples; for (int sample = 0; sample < nsamples; ++sample, pos += invsamples) { Imath::V3f Rsamp = R + pos*Rmajor; float s, t; vector_to_latlong (Rsamp, texturefile->m_y_up, s, t); // Determine the MIP-map level(s) we need: we will blend // data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend int miplevel[2] = { -1, -1 }; float levelblend = 0; int nmiplevels = (int)subinfo.levels.size(); for (int m = 0; m < nmiplevels; ++m) { // Compute the filter size in raster space at this MIP level. // Filters are in radians, and the vertical resolution of a // latlong map is PI radians. So to compute the raster size of // our filter width... float filtwidth_ras = subinfo.spec(m).full_height * filtwidth * M_1_PI; // Once the filter width is smaller than one texel at this level, // we've gone too far, so we know that we want to interpolate the // previous level and the current level. Note that filtwidth_ras // is expected to be >= 0.5, or would have stopped one level ago. if (filtwidth_ras <= 1) { miplevel[0] = m-1; miplevel[1] = m; levelblend = Imath::clamp (2.0f - 1.0f/filtwidth_ras, 0.0f, 1.0f); break; } } if (miplevel[1] < 0) { // We'd like to blur even more, but make due with the coarsest // MIP level. miplevel[0] = nmiplevels - 1; miplevel[1] = miplevel[0]; levelblend = 0; } else if (miplevel[0] < 0) { // We wish we had even more resolution than the finest MIP level, // but tough for us. miplevel[0] = 0; miplevel[1] = 0; levelblend = 0; } if (options.mipmode == TextureOpt::MipModeOneLevel) { // Force use of just one mipmap level miplevel[1] = miplevel[0]; levelblend = 0; } else if (mipmode == TextureOpt::MipModeNoMIP) { // Just sample from lowest level miplevel[0] = 0; miplevel[1] = 0; levelblend = 0; } float levelweight[2] = { 1.0f - levelblend, levelblend }; int npointson = 0; for (int level = 0; level < 2; ++level) { if (! levelweight[level]) continue; ++npointson; int lev = miplevel[level]; if (options.interpmode == TextureOpt::InterpSmartBicubic) { if (lev == 0 || (texturefile->spec(options.subimage,lev).full_height < naturalres/2)) { accumer = &TextureSystemImpl::accum_sample_bicubic; ++stats.cubic_interps; } else { accumer = &TextureSystemImpl::accum_sample_bilinear; ++stats.bilinear_interps; } } else { *probecount += 1; } ok &= (this->*accumer) (s, t, miplevel[level], *texturefile, thread_info, options, levelweight[level]*invsamples, result, dresultds, dresultdt); } } stats.aniso_probes += nsamples; ++stats.aniso_queries; if (actualchannels < options.nchannels) fill_channels (spec, options, result); return ok; }
/// Implementation derived from Wild Magic (Version 2) Software Library, available /// from http://www.geometrictools.com/Downloads/WildMagic2p5.zip under free license bool SpherePrimitiveEvaluator::intersectionPoint( const Imath::V3f &origin, const Imath::V3f &direction, PrimitiveEvaluator::Result *result, float maxDistance ) const { assert( dynamic_cast<Result *>( result ) ); Result *sr = static_cast<Result *>( result ); Imath::V3f dir = direction.normalized(); (void)direction; float a0 = origin.dot( origin ) - m_sphere->radius() * m_sphere->radius(); float a1 = dir.dot( origin ); float discr = a1 * a1 - a0; if (discr < 0.0) { return false; } if ( discr >= Imath::limits<float>::epsilon() ) { float root = sqrt( discr ); float t0 = -a1 - root; float t1 = -a1 + root; Imath::V3f p0 = origin + t0 * dir; Imath::V3f p1 = origin + t1 * dir; if ( t0 >= 0.0 ) { if ( t1 >= 0.0 ) { if ( (origin - p0).length2() < ( origin - p1 ).length2() ) { sr->m_p = p0; } else { sr->m_p = p1; } } else { sr->m_p = p0; } } else if ( t1 >= 0.0 ) { sr->m_p = p1; } else { return false; } } else { float t = -a1; if ( t >= 0.0 ) { sr->m_p = origin + t * dir; } else { return false; } } if ( (sr->m_p - origin).length() < maxDistance) { return true; } return false; }