/// 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();
}
예제 #2
0
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;
}