/** \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(); }
/** \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(); }