Ejemplo n.º 1
0
/** 3D float Perlin noise.
 */
TqFloat	CqNoise::FGNoise3( const CqVector3D& v )
{
	TqFloat x, y, z;
	x = v.x();
	y = v.y();
	z = v.z();
	return ( 0.5f * ( 1.0f + CqNoise1234::noise( x, y, z ) ) );
}
Ejemplo n.º 2
0
/** Vector-valued 4D Perlin noise.
 */
CqVector3D CqNoise::PGNoise4( const CqVector3D& v, TqFloat t )
{
	TqFloat x, y, z;
	TqFloat a, b, c;
	x = v.x();
	y = v.y();
	z = v.z();
	a = 0.5f * ( 1.0f + CqNoise1234::noise( x, y, z, t ) );
	b = 0.5f * ( 1.0f + CqNoise1234::noise( x + O1x, y + O1y, z + O1z, t + O1t ) );
	c = 0.5f * ( 1.0f + CqNoise1234::noise( x + O2x, y + O2y, z + O2z, t + O2t ) );
	return ( CqVector3D( a, b, c ) );
}
Ejemplo n.º 3
0
// Dump a 3d vector
void CqMPDump::dumpVec3(const CqVector3D& v)
{
	TqFloat x = v.x();
	TqFloat y = v.y();
	TqFloat z = v.z();

	size_t len_written = fwrite((void*)&x, sizeof(TqFloat), 1, m_outFile);
	len_written += fwrite((void*)&y, sizeof(TqFloat), 1, m_outFile);
	len_written += fwrite((void*)&z, sizeof(TqFloat), 1, m_outFile);
	if(len_written != 3)
		AQSIS_THROW_XQERROR(XqInvalidFile, EqE_System,
				"Error writing mpdump file");
}
void CqEnvironmentMapOld::SampleMap( CqVector3D& R1,
                                  CqVector3D& swidth, CqVector3D& twidth,
                                  std::valarray<TqFloat>& val, TqInt index,
                                  TqFloat* average_depth, TqFloat* shadow_depth )
{
	// Check the memory and make sure we don't abuse it
	CriticalMeasure();

	if ( m_pImage != 0 )
	{

		if ( Type() != MapType_LatLong )
		{
			CqVector3D	R2, R3, R4;
			R2 = R1 + swidth;
			R3 = R1 + twidth;
			R4 = R1 + swidth + twidth;
			SampleMap( R1, R2, R3, R4, val );
		}
		else if ( Type() == MapType_LatLong )
		{
			CqVector3D V = R1;
			V.Unit();
			TqFloat sswidth = swidth.Magnitude();
			TqFloat stwidth = twidth.Magnitude();

			TqFloat ss1 = atan2( V.y(), V.x() ) / ( 2.0 * RI_PI );  // -.5 -> .5
			ss1 = ss1 + 0.5; // remaps to 0 -> 1
			TqFloat tt1 = acos( V.z() ) / RI_PI;

			CqTextureMapOld::SampleMap( ss1, tt1, sswidth/RI_PI, stwidth/RI_PI, val );
		}
	}
}
Ejemplo n.º 5
0
		/** \brief Compute occlusion from the current view direction to the
		 * given sample region.
		 *
		 * \param sampleRegion - parallelogram region over which to sample the map
		 * \param sampleOpts - set of sampling options 
		 * \param numSamples - number of samples to take for the region.
		 * \param outSamps[0] - Return parameter; amount of occlusion over the
		 *                      sample region in the viewing direction for this map.
		 */
		void sample(const Sq3DSamplePllgram& sampleRegion,
				const CqShadowSampleOptions& sampleOpts,
				const TqInt numSamples, TqFloat* outSamps)
		{
			// filter weights
			CqConstFilter filterWeights;
			// Use constant depth approximation for the surface for maximum
			// sampling speed.  We use the depth from the camera to the centre
			// of the sample region.
			CqConstDepthApprox depthFunc((m_currToLight*sampleRegion.c).z());
			// Determine rough filter support.  This results in a
			// texture-aligned box, so doesn't do proper anisotropic filtering.
			// For occlusion this isn't visible anyway because of the large
			// amount of averaging.  We also want the filter setup to be as
			// fast as possible.
//			CqVector3D side1 = m_currToRasterVec*sampleRegion.s1;
//			CqVector3D side2 = m_currToRasterVec*sampleRegion.s2;
//			CqVector3D center = m_currToRaster*sampleRegion.c;
//			TqFloat sWidthOn2 = max(side1.x(), side2.x())*m_pixels.width()/2;
//			TqFloat tWidthOn2 = max(side1.y(), side2.y())*m_pixels.height()/2;

			// TODO: Fix the above calculation so that the width is actually
			// taken into account properly.
			CqVector3D center = m_currToRaster*sampleRegion.c;
			TqFloat sWidthOn2 = 0.5*(sampleOpts.sBlur()*m_pixels.width());
			TqFloat tWidthOn2 = 0.5*(sampleOpts.tBlur()*m_pixels.height());
			SqFilterSupport support(
					lround(center.x()-sWidthOn2), lround(center.x()+sWidthOn2) + 1,
					lround(center.y()-tWidthOn2), lround(center.y()+tWidthOn2) + 1);
			// percentage closer accumulator
			CqPcfAccum<CqConstFilter, CqConstDepthApprox> accumulator(
					filterWeights, depthFunc, sampleOpts.startChannel(),
					sampleOpts.biasLow(), sampleOpts.biasHigh(), outSamps);
			// accumulate occlusion over the filter support.
			filterTextureNowrapStochastic(accumulator, m_pixels, support, numSamples);
		}
Ejemplo n.º 6
0
/** 3D float Perlin periodic noise.
 */
TqFloat	CqNoise::FGPNoise3( const CqVector3D& v, const CqVector3D& pv )
{
	TqFloat x, y, z;
	TqFloat pfx, pfy, pfz;
	TqInt px, py, pz;
	x = v.x();
	y = v.y();
	z = v.z();
	pfx = pv.x() + 0.5f; // Temp variables to avoid having the FASTFLOOR() macro
	pfy = pv.y() + 0.5f; // expand to something that is difficult to optimise.
	pfz = pv.z() + 0.5f; // (An inline function fastfloor() would be nicer.)
	px = FASTFLOOR( pfx );
	py = FASTFLOOR( pfy );
	pz = FASTFLOOR( pfz );
	return ( 0.5f * ( 1.0f + CqNoise1234::pnoise( x, y, z, px, py, pz ) ) );
}
Ejemplo n.º 7
0
/** 4D float Perlin periodic noise.
 */
TqFloat	CqNoise::FGPNoise4( const CqVector3D& v, TqFloat t, const CqVector3D& pv, TqFloat pft )
{
	TqFloat x, y, z;
	TqFloat pfx, pfy, pfz;
	TqInt px, py, pz, pt;
	x = v.x();
	y = v.y();
	z = v.z();
	pfx = pv.x() + 0.5f; // Temp variables to avoid having the FASTFLOOR() macro
	pfy = pv.y() + 0.5f; // expand to something that is difficult to optimise.
	pfz = pv.z() + 0.5f;
	pft = pft + 0.5f;
	px = FASTFLOOR( pfx );
	py = FASTFLOOR( pfy );
	pz = FASTFLOOR( pfz );
	pt = FASTFLOOR( pft );
	return ( 0.5f * ( 1.0f + CqNoise1234::pnoise( x, y, z, t, px, py, pz, pt ) ) );
}
Ejemplo n.º 8
0
/** Vector-valued 3D Perlin periodic noise.
 */
CqVector3D CqNoise::PGPNoise3( const CqVector3D& v, const CqVector3D& pv )
{
	TqFloat x, y, z;
	TqFloat a, b, c;
	TqFloat pfx, pfy, pfz;
	TqInt px, py, pz;
	x = v.x();
	y = v.y();
	z = v.z();
	pfx = pv.x() + 0.5f; // Temp variables to avoid having the FASTFLOOR() macro
	pfy = pv.y() + 0.5f; // expand to something that is difficult to optimise.
	pfz = pv.z() + 0.5f; // This might seem stupid, but it *is* actually faster.
	px = FASTFLOOR(pfx);
	py = FASTFLOOR(pfy);
	pz = FASTFLOOR(pfz);
	a = 0.5f * ( 1.0f + CqNoise1234::pnoise( x, y, z, px, py, pz ) );
	b = 0.5f * ( 1.0f + CqNoise1234::pnoise( x + O1x, y + O1y, z + O1z, px, py, pz ) );
	c = 0.5f * ( 1.0f + CqNoise1234::pnoise( x + O2x, y + O2y, z + O2z, px, py, pz ) );
	return ( CqVector3D ( a, b, c ) );
}
Ejemplo n.º 9
0
		/** \brief Create a view from imageNum of the provided file.
		 *
		 * \param file - file from which to read the image data.
		 * \param imageNum - subimage number for this view in the input file
		 * \param currToWorld - current -> world transformation matrix.
		 */
		CqShadowView(const boost::shared_ptr<IqTiledTexInputFile>& file, TqInt imageNum,
				const CqMatrix& currToWorld)
			: m_currToLight(),
			m_currToRaster(),
			m_currToRasterVec(),
			m_viewDirec(),
			m_pixels(file, imageNum)
		{
			// TODO refactor with CqShadowSampler, also refactor this function,
			// since it's a bit unweildly...
			if(!file)
				AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
						"Cannot construct shadow map from NULL file handle");

			const CqTexFileHeader& header = file->header(imageNum);
			if(header.channelList().sharedChannelType() != Channel_Float32)
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"Shadow maps must hold 32-bit floating point data");

			// Get matrix which transforms the sample points to the light
			// camera coordinates.
			const CqMatrix* worldToLight
				= header.findPtr<Attr::WorldToCameraMatrix>();
			if(!worldToLight)
			{
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"No world -> camera matrix found in file \""
						<< file->fileName() << "\"");
			}
			m_currToLight = (*worldToLight) * currToWorld;

			// Get matrix which transforms the sample points to texture coordinates.
			const CqMatrix* worldToLightScreen
				= header.findPtr<Attr::WorldToScreenMatrix>();
			if(!worldToLightScreen)
			{
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"No world -> screen matrix found in file \""
						<< file->fileName() << "\"");
			}
			m_currToRaster = (*worldToLightScreen) * currToWorld;
			// worldToLightScreen transforms world coordinates to "screen" coordinates,
			// ie, onto the 2D box [-1,1]x[-1,1].  We instead want texture coordinates,
			// which correspond to the box [0,width]x[0,height].  In
			// addition, the direction of increase of the y-axis should be
			// swapped, since texture coordinates define the origin to be in
			// the top left of the texture rather than the bottom right.
			m_currToRaster.Translate(CqVector3D(1,-1,0));
			m_currToRaster.Scale(0.5f, -0.5f, 1);

			// Transform the light origin to "current" space to use
			// when checking the visibility of a point.
			m_lightPos = m_currToLight.Inverse()*CqVector3D(0,0,0);
			// Transform the normal (0,0,1) in light space into a normal in
			// "current" space.  The appropriate matrix is the inverse of the
			// cam -> light normal transformation, which itself is the inverse
			// transpose of currToLightVec.
			CqMatrix currToLightVec = m_currToLight;
			currToLightVec[3][0] = 0;
			currToLightVec[3][1] = 0;
			currToLightVec[3][2] = 0;
			m_viewDirec = currToLightVec.Transpose()*CqVector3D(0,0,1);
			m_viewDirec.Unit();
		}
Ejemplo n.º 10
0
/**
 * Calculates bounds for a CqCurve.
 *
 * NOTE: This method makes the same assumptions as 
 * CqSurfacePatchBicubic::Bound() does about the convex-hull property of the
 * curve.  This is fine most of the time, but the user can specify basis
 * matrices like Catmull-Rom, which are non-convex.
 *
 * FIXME: Make sure that all hulls which reach this method are convex!
 *
 * @return CqBound object containing the bounds.
 */
void CqCurve::Bound(CqBound* bound) const
{

	// Get the boundary in camera space.
	CqVector3D vecA( FLT_MAX, FLT_MAX, FLT_MAX );
	CqVector3D vecB( -FLT_MAX, -FLT_MAX, -FLT_MAX );
	TqFloat maxCameraSpaceWidth = 0;
	TqUint nWidthParams = cVarying();
	for ( TqUint i = 0; i < ( *P() ).Size(); i++ )
	{
		// expand the boundary if necessary to accomodate the
		//  current vertex
		CqVector3D vecV = vectorCast<CqVector3D>(P()->pValue( i )[0]);
		if ( vecV.x() < vecA.x() )
			vecA.x( vecV.x() );
		if ( vecV.y() < vecA.y() )
			vecA.y( vecV.y() );
		if ( vecV.x() > vecB.x() )
			vecB.x( vecV.x() );
		if ( vecV.y() > vecB.y() )
			vecB.y( vecV.y() );
		if ( vecV.z() < vecA.z() )
			vecA.z( vecV.z() );
		if ( vecV.z() > vecB.z() )
			vecB.z( vecV.z() );

		// increase the maximum camera space width of the curve if
		//  necessary
		if ( i < nWidthParams )
		{
			TqFloat camSpaceWidth = width()->pValue( i )[0];
			if ( camSpaceWidth > maxCameraSpaceWidth )
			{
				maxCameraSpaceWidth = camSpaceWidth;
			}
		}

	}

	// increase the size of the boundary by half the width of the
	//  curve in camera space
	vecA -= ( maxCameraSpaceWidth / 2.0 );
	vecB += ( maxCameraSpaceWidth / 2.0 );

	bound->vecMin() = vecA;
	bound->vecMax() = vecB;
	AdjustBoundForTransformationMotion( bound );
}
Ejemplo n.º 11
0
		/** \brief Create a view from imageNum of the provided file.
		 *
		 * \param file - file from which to read the image data.
		 * \param imageNum - subimage number for this view in the input file
		 * \param currToWorld - current -> world transformation matrix.
		 */
		CqOccView(const boost::shared_ptr<IqTiledTexInputFile>& file, TqInt imageNum,
				const CqMatrix& currToWorld)
			: m_currToLight(),
			m_currToRaster(),
			m_currToRasterVec(),
			m_negViewDirec(),
			m_pixels(file, imageNum)
		{
			// TODO refactor with CqShadowSampler, also refactor this function,
			// since it's a bit unweildly...
			if(!file)
				AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
						"Cannot construct shadow map from NULL file handle");

			const CqTexFileHeader& header = file->header(imageNum);
			if(header.channelList().sharedChannelType() != Channel_Float32)
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"Shadow maps must hold 32-bit floating point data");

			// Get matrix which transforms the sample points to the light
			// camera coordinates.
			const CqMatrix* worldToLight
				= header.findPtr<Attr::WorldToCameraMatrix>();
			if(!worldToLight)
			{
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"No world -> camera matrix found in file \""
						<< file->fileName() << "\"");
			}
			m_currToLight = (*worldToLight) * currToWorld;

			// Get matrix which transforms the sample points to texture coordinates.
			const CqMatrix* worldToLightScreen
				= header.findPtr<Attr::WorldToScreenMatrix>();
			if(!worldToLightScreen)
			{
				AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
						"No world -> screen matrix found in file \""
						<< file->fileName() << "\"");
			}
			m_currToRaster = (*worldToLightScreen) * currToWorld;
			// worldToLightScreen transforms world coordinates to "screen" coordinates,
			// ie, onto the 2D box [-1,1]x[-1,1].  We instead want texture coordinates,
			// which correspond to the box [0,width]x[0,height].  In
			// addition, the direction of increase of the y-axis should be
			// swapped, since texture coordinates define the origin to be in
			// the top left of the texture rather than the bottom right.
			m_currToRaster.Translate(CqVector3D(1,-1,0));
			m_currToRaster.Scale(0.5f*header.width(), -0.5f*header.height(), 1);
			// This extra translation is by half a pixel width - it moves the
			// raster coordinates 
			m_currToRaster.Translate(CqVector3D(-0.5,-0.5,0));

			// Convert current -> texture transformation into a vector
			// transform rather than a point transform.
			// TODO: Put this stuff into the CqMatrix class?
			m_currToRasterVec = m_currToRaster;
			m_currToRasterVec[3][0] = 0;
			m_currToRasterVec[3][1] = 0;
			m_currToRasterVec[3][2] = 0;
			// This only really makes sense when the matrix is affine rather
			// than projective, ie, the last column is (0,0,0,h) 
			//
			// TODO: Investigate whether this is really correct.
//			assert(m_currToRasterVec[0][3] == 0);
//			assert(m_currToRasterVec[1][3] == 0);
//			assert(m_currToRasterVec[2][3] == 0);
			m_currToRasterVec[0][3] = 0;
			m_currToRasterVec[1][3] = 0;
			m_currToRasterVec[2][3] = 0;

			// Transform the normal (0,0,1) in light space into a normal in
			// "current" space.  The appropriate matrix is the inverse of the
			// cam -> light normal transformation, which itself is the inverse
			// transpose of currToLightVec.
			CqMatrix currToLightVec = m_currToLight;
			currToLightVec[3][0] = 0;
			currToLightVec[3][1] = 0;
			currToLightVec[3][2] = 0;
			m_negViewDirec = currToLightVec.Transpose()*CqVector3D(0,0,-1);
			m_negViewDirec.Unit();
		}
Ejemplo n.º 12
0
void CqOcclusionSampler::sample(const Sq3DSamplePllgram& samplePllgram,
		const CqVector3D& normal, const CqShadowSampleOptions& sampleOpts,
		TqFloat* outSamps) const
{
	assert(sampleOpts.numChannels() == 1);

	// Unit normal indicating the hemisphere to sample for occlusion.
	CqVector3D N = normal;
	N.Unit();

	const TqFloat sampNumMult = 4.0 * sampleOpts.numSamples() / m_maps.size();

	// Accumulate the total occlusion over all directions.  Here we use an
	// importance sampling approach: we decide how many samples each map should
	// have based on it's relative importance as measured by the map weight.
	TqFloat totOcc = 0;
	TqInt totNumSamples = 0;
	TqFloat maxWeight = 0;
	TqViewVec::const_iterator maxWeightMap = m_maps.begin();
	for(TqViewVec::const_iterator map = m_maps.begin(), end = m_maps.end();
			map != end; ++map)
	{
		TqFloat weight = (*map)->weight(N);
		if(weight > 0)
		{
			// Compute the number of samples to use.  Assuming that the shadow
			// maps are spread evenly over the sphere, we have an area of 
			//
			//    4*PI / m_maps.size()
			//
			// steradians per map.  The density of sample points per steradian
			// should be
			//
			//    sampleOpts.numSamples() * weight / PI
			//
			// Therefore the expected number of samples per map is
			TqFloat numSampFlt = sampNumMult*weight;
			// This isn't an integer though, so we take the floor,
			TqInt numSamples = lfloor(numSampFlt);
			// TODO: Investigate performance impact of using RandomFloat() here.
			if(m_random.RandomFloat() < numSampFlt - numSamples)
			{
				// And increment with a probability equal to the extra fraction
				// of samples that the current map should have.
				++numSamples;
			}
			if(numSamples > 0)
			{
				// Compute amount of occlusion from the current view.
				TqFloat occ = 0;
				(*map)->sample(samplePllgram, sampleOpts, numSamples, &occ);
				// Accumulate into total occlusion and weight.
				totOcc += occ*numSamples;
				totNumSamples += numSamples;
			}
			if(weight > maxWeight)
			{
				maxWeight = weight;
				maxWeightMap = map;
			}
		}
	}

	// The algorithm above sometimes results in no samples being computed for
	// low total sample numbers.  Here we attempt to allow very small numbers
	// of samples to be useful by sampling the most highly weighted map if no
	// samples have been taken
	if(totNumSamples == 0 && maxWeight > 0)
	{
		TqFloat occ = 0;
		(*maxWeightMap)->sample(samplePllgram, sampleOpts, 1, &occ);
		totOcc += occ;
		totNumSamples += 1;
	}

	// Normalize the sample
	*outSamps = totOcc / totNumSamples;
}