//----------------------------------------------------------------------------- // Generate spheremap based on the current images (only works for cubemaps) // The look dir indicates the direction of the center of the sphere //----------------------------------------------------------------------------- void CVTFTexture::GenerateSpheremap( LookDir_t lookDir ) { if (!IsCubeMap()) return; Assert( m_Format == IMAGE_FORMAT_RGBA8888 ); // We'll be doing our work in IMAGE_FORMAT_RGBA8888 mode 'cause it's easier unsigned char *pCubeMaps[6]; // Allocate the bits for the spheremap int iMemRequired = ImageLoader::GetMemRequired( m_nWidth, m_nHeight, IMAGE_FORMAT_RGBA8888, false ); unsigned char *pSphereMapBits = (unsigned char *)MemAllocScratch(iMemRequired); // Generate a spheremap for each frame of the cubemap for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame) { // Point to our own textures (highest mip level) for (int iFace = 0; iFace < 6; ++iFace) { pCubeMaps[iFace] = ImageData( iFrame, iFace, 0 ); } // Compute the spheremap of the top LOD ComputeSpheremapFrame( pCubeMaps, pSphereMapBits, lookDir ); // Compute the mip levels of the spheremap, converting from RGBA8888 to our format unsigned char *pFinalSphereMapBits = ImageData( iFrame, CUBEMAP_FACE_SPHEREMAP, 0 ); ImageLoader::GenerateMipmapLevels( pSphereMapBits, pFinalSphereMapBits, m_nWidth, m_nHeight, m_Format, 2.2, 2.2 ); } // Free memory MemFreeScratch(); }
//----------------------------------------------------------------------------- // Gets the texture all internally consistent assuming you've loaded // mip 0 of all faces of all frames //----------------------------------------------------------------------------- void CVTFTexture::PostProcess(bool bGenerateSpheremap, LookDir_t lookDir) { Assert( m_Format == IMAGE_FORMAT_RGBA8888 ); // Set up the cube map faces if (IsCubeMap()) { // Rotate the cubemaps so they're appropriate for the material system FixCubemapFaceOrientation(); // FIXME: We could theoretically not compute spheremap mip levels // in generate spheremaps; should we? The trick is when external // clients can be expected to call it // Compute the spheremap fallback for cubemaps if we weren't able to load up one... if (bGenerateSpheremap) GenerateSpheremap(lookDir); } // Generate mipmap levels GenerateMipmaps(); if( Flags() & TEXTUREFLAGS_ONEOVERMIPLEVELINALPHA ) { PutOneOverMipLevelInAlpha(); } // Compute reflectivity ComputeReflectivity(); // Are we 8-bit or 1-bit alpha? // NOTE: We have to do this *after* computing the spheremap fallback for // cubemaps or it'll throw the flags off ComputeAlphaFlags(); }
void CDxtexDoc::OnFormatChangeSurfaceFmt() { CChangeFmtDlg changeFmtDlg; LPDIRECT3DBASETEXTURE9 ptex; ptex = (m_ptexNew == NULL ? m_ptexOrig : m_ptexNew); if (IsVolumeMap()) { D3DVOLUME_DESC vd; ((LPDIRECT3DVOLUMETEXTURE9)ptex)->GetLevelDesc(0, &vd); changeFmtDlg.m_fmt = vd.Format; } else if (IsCubeMap()) { D3DSURFACE_DESC sd; ((LPDIRECT3DCUBETEXTURE9)ptex)->GetLevelDesc(0, &sd); changeFmtDlg.m_fmt = sd.Format; } else { D3DSURFACE_DESC sd; ((LPDIRECT3DTEXTURE9)ptex)->GetLevelDesc(0, &sd); changeFmtDlg.m_fmt = sd.Format; } changeFmtDlg.m_bVolume = IsVolumeMap(); if (IDCANCEL == changeFmtDlg.DoModal()) return; Compress(changeFmtDlg.m_fmt, TRUE); }
D3DFORMAT CDxtexDoc::GetFormat(LPDIRECT3DBASETEXTURE9 ptex) { LPDIRECT3DTEXTURE9 pmiptex = NULL; LPDIRECT3DCUBETEXTURE9 pcubetex = NULL; LPDIRECT3DVOLUMETEXTURE9 pvoltex = NULL; D3DFORMAT fmt = D3DFMT_UNKNOWN; if (IsVolumeMap()) pvoltex = (LPDIRECT3DVOLUMETEXTURE9)ptex; else if (IsCubeMap()) pcubetex = (LPDIRECT3DCUBETEXTURE9)ptex; else pmiptex = (LPDIRECT3DTEXTURE9)ptex; if (pvoltex != NULL) { D3DVOLUME_DESC vd; pvoltex->GetLevelDesc(0, &vd); fmt = vd.Format; } else if (pcubetex != NULL) { D3DSURFACE_DESC sd; pcubetex->GetLevelDesc(0, &sd); fmt = sd.Format; } else if( pmiptex != NULL ) { D3DSURFACE_DESC sd; pmiptex->GetLevelDesc(0, &sd); fmt = sd.Format; } return fmt; }
void CDxtexDoc::OpenAlphaCubeFace(D3DCUBEMAP_FACES FaceType) { HRESULT hr; CString fileName; LPDIRECT3DSURFACE9 psurfOrig = NULL; LPDIRECT3DSURFACE9 psurfNew = NULL; D3DSURFACE_DESC sd; if (!IsCubeMap()) return; hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetCubeMapSurface(FaceType, 0, &psurfOrig); ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetLevelDesc(0, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); return; } if (m_ptexNew != NULL) { hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetCubeMapSurface(FaceType, 0, &psurfNew); } if (!PromptForBmp(&fileName)) return; if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfOrig))) return; if (psurfNew != NULL) { if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfNew))) return; } if (m_numMips > 1) { hr = D3DXFilterCubeTexture((LPDIRECT3DCUBETEXTURE9)m_ptexOrig, NULL, 0, D3DX_DEFAULT); } if (psurfNew != NULL) { hr = D3DXLoadSurfaceFromSurface(psurfNew, NULL, NULL, psurfOrig, NULL, NULL, D3DX_DEFAULT, 0); if (m_numMips > 1) { hr = D3DXFilterCubeTexture((LPDIRECT3DCUBETEXTURE9)m_ptexNew, NULL, 0, D3DX_DEFAULT); } } ReleasePpo(&psurfOrig); ReleasePpo(&psurfNew); SetModifiedFlag(TRUE); UpdateAllViews(NULL, 1); }
//----------------------------------------------------------------------------- // Unserialization of image data //----------------------------------------------------------------------------- bool CVTFTexture::LoadImageData( CUtlBuffer &buf, const VTFFileHeader_t &header, int nSkipMipLevels ) { // Fix up the mip count + size based on how many mip levels we skip... if (nSkipMipLevels > 0) { Assert( m_nMipCount > nSkipMipLevels ); if (header.numMipLevels < nSkipMipLevels) { // NOTE: This can only happen with older format .vtf files Warning("Warning! Encountered old format VTF file; please rebuild it!\n"); return false; } ComputeMipLevelDimensions( nSkipMipLevels, &m_nWidth, &m_nHeight ); m_nMipCount -= nSkipMipLevels; } // read the texture image (including mipmaps if they are there and needed.) int iImageSize = ComputeFaceSize( ); iImageSize *= m_nFaceCount * m_nFrameCount; // For backwards compatibility, we don't read in the spheremap fallback on // older format .VTF files... int nFacesToRead = m_nFaceCount; if (IsCubeMap()) { if ((header.version[0] == 7) && (header.version[1] < 1)) nFacesToRead = 6; } // NOTE: We load the bits this way because we store the bits in memory // differently that the way they are stored on disk; we store on disk // differently so we can only load up // NOTE: The smallest mip levels are stored first!! AllocateImageData( iImageSize ); for (int iMip = m_nMipCount; --iMip >= 0; ) { // NOTE: This is for older versions... if (header.numMipLevels - nSkipMipLevels <= iMip) continue; int iMipSize = ComputeMipSize( iMip ); for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame) { for (int iFace = 0; iFace < nFacesToRead; ++iFace) { unsigned char *pMipBits = ImageData( iFrame, iFace, iMip ); buf.Get( pMipBits, iMipSize ); } } } return buf.IsValid(); }
void CDxtexDoc::OpenCubeFace(D3DCUBEMAP_FACES FaceType) { HRESULT hr; CString fileName; LPDIRECT3DSURFACE9 psurfOrig = NULL; LPDIRECT3DSURFACE9 psurfNew = NULL; if (!IsCubeMap()) return; hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetCubeMapSurface(FaceType, 0, &psurfOrig); if (m_ptexNew != NULL) hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetCubeMapSurface(FaceType, 0, &psurfNew); if (!PromptForBmp(&fileName)) return; hr = D3DXLoadSurfaceFromFile(psurfOrig, NULL, NULL, fileName, NULL, D3DX_DEFAULT, 0, NULL); // Look for "foo_a.bmp" for alpha channel int i = fileName.ReverseFind('.'); fileName = fileName.Left(i) + "_a.bmp"; CFileStatus status; if (CFile::GetStatus(fileName, status)) { if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfOrig))) return; } if (m_numMips > 1) { hr = D3DXFilterCubeTexture((LPDIRECT3DCUBETEXTURE9)m_ptexOrig, NULL, 0, D3DX_DEFAULT); } if (psurfNew != NULL) { hr = D3DXLoadSurfaceFromSurface(psurfNew, NULL, NULL, psurfOrig, NULL, NULL, D3DX_DEFAULT, 0); if (m_numMips > 1) { hr = D3DXFilterCubeTexture((LPDIRECT3DCUBETEXTURE9)m_ptexNew, NULL, 0, D3DX_DEFAULT); } } ReleasePpo(&psurfOrig); ReleasePpo(&psurfNew); SetModifiedFlag(TRUE); UpdateAllViews(NULL, 1); }
bool WebGLTexture::BindTexture(TexTarget texTarget) { if (IsDeleted()) { mContext->ErrorInvalidOperation("bindTexture: Cannot bind a deleted object."); return false; } const bool isFirstBinding = !HasEverBeenBound(); if (!isFirstBinding && mTarget != texTarget) { mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound" " to a different target."); return false; } mTarget = texTarget; mContext->gl->fBindTexture(mTarget.get(), mGLName); if (isFirstBinding) { mFaceCount = IsCubeMap() ? 6 : 1; gl::GLContext* gl = mContext->gl; // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R // is not present in GLES 2, but is present in GL and it seems as if for // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected // GLES behavior. // If we are WebGL 2 though, we'll want to leave it as REPEAT. const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D); if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) { gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); } } return true; }
void CVTFTexture::ImageFileInfo( int nFrame, int nFace, int nMipLevel, int *pStartLocation, int *pSizeInBytes) const { int i; int iMipWidth; int iMipHeight; // The image data starts after the low-res image int iLowResSize; int nOffset; LowResFileInfo( &nOffset, &iLowResSize ); nOffset += iLowResSize; // get to the right miplevel for( i = m_nMipCount - 1; i > nMipLevel; --i ) { ComputeMipLevelDimensions( i, &iMipWidth, &iMipHeight ); int iMipLevelSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, m_Format, false ); nOffset += iMipLevelSize * m_nFrameCount * m_nFaceCount; } // get to the right frame ComputeMipLevelDimensions( nMipLevel, &iMipWidth, &iMipHeight ); int nFaceSize = ImageLoader::GetMemRequired( iMipWidth, iMipHeight, m_Format, false ); // For backwards compatibility, we don't read in the spheremap fallback on // older format .VTF files... int nFacesToRead = m_nFaceCount; if (IsCubeMap()) { if ((m_pVersion[0] == 7) && (m_pVersion[1] < 1)) { nFacesToRead = 6; if (nFace == CUBEMAP_FACE_SPHEREMAP) --nFace; } } int nFrameSize = nFacesToRead * nFaceSize; nOffset += nFrameSize * nFrame; // get to the right face nOffset += nFace * nFaceSize; *pStartLocation = nOffset; *pSizeInBytes = nFaceSize; }
HRESULT CDxtexDoc::LoadAlphaBmp(CString& strPath) { HRESULT hr; LPDIRECT3DTEXTURE9 pmiptex; LPDIRECT3DSURFACE9 psurf; if (IsCubeMap()) return E_FAIL; pmiptex = (LPDIRECT3DTEXTURE9)m_ptexOrig; hr = pmiptex->GetSurfaceLevel(0, &psurf); if (FAILED(hr)) return hr; hr = LoadAlphaIntoSurface(strPath, psurf); ReleasePpo(&psurf); if (FAILED(hr)) return hr; UpdateAllViews(NULL, 1); // tell CView to pick up new surface pointers return S_OK; }
//----------------------------------------------------------------------------- // Fixes the cubemap faces orientation from our standard to what the material system needs //----------------------------------------------------------------------------- void CVTFTexture::FixCubemapFaceOrientation( ) { if (!IsCubeMap()) return; Assert( m_Format == IMAGE_FORMAT_RGBA8888 ); for (int iMipLevel = 0; iMipLevel < m_nMipCount; ++iMipLevel) { int iMipSize, iTemp; ComputeMipLevelDimensions( iMipLevel, &iMipSize, &iTemp ); Assert( iMipSize == iTemp ); for (int iFrame = 0; iFrame < m_nFrameCount; ++iFrame) { for (int iFace = 0; iFace < 6; ++iFace) { FixCubeMapFacing( ImageData( iFrame, iFace, iMipLevel ), iFace, iMipSize, m_Format ); } } } }
void CDxtexDoc::OnUpdateFileOpenAlpha(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsCubeMap() && !IsVolumeMap()); }
void CDxtexDoc::OpenAlphaSubsurface(D3DCUBEMAP_FACES FaceType, LONG lwMip, LONG lwSlice) { HRESULT hr; CString fileName; LPDIRECT3DDEVICE9 pd3ddev = PDxtexApp()->Pd3ddev(); LPDIRECT3DTEXTURE9 ptexOrig = NULL; LPDIRECT3DTEXTURE9 ptexNew = NULL; LPDIRECT3DSURFACE9 psurfOrig = NULL; LPDIRECT3DSURFACE9 psurfNew = NULL; LPDIRECT3DVOLUME9 pvolOrig = NULL; LPDIRECT3DVOLUME9 pvolNew = NULL; D3DSURFACE_DESC sd; DWORD dwWidth = m_dwWidth; DWORD dwHeight = m_dwHeight; if (IsVolumeMap()) { for (int i = 0; i < lwMip; i++) { dwWidth /= 2; dwHeight /= 2; } hr = pd3ddev->CreateTexture(dwWidth, dwHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &ptexOrig, NULL); hr = ptexOrig->GetSurfaceLevel(0, &psurfOrig); hr = ((LPDIRECT3DVOLUMETEXTURE9)m_ptexOrig)->GetVolumeLevel(lwMip, &pvolOrig); hr = LoadSurfaceFromVolumeSlice(pvolOrig, lwSlice, psurfOrig); if (m_ptexNew != NULL) { hr = pd3ddev->CreateTexture(dwWidth, dwHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &ptexNew, NULL); hr = ptexNew->GetSurfaceLevel(0, &psurfOrig); hr = ((LPDIRECT3DVOLUMETEXTURE9)m_ptexOrig)->GetVolumeLevel(lwMip, &pvolNew); hr = LoadSurfaceFromVolumeSlice(pvolNew, lwSlice, psurfOrig); } } else if (IsCubeMap()) { hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetCubeMapSurface(FaceType, lwMip, &psurfOrig); ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetLevelDesc(lwMip, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); goto LCleanup; } if (m_ptexNew != NULL) { hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetCubeMapSurface(FaceType, lwMip, &psurfNew); ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetLevelDesc(lwMip, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); goto LCleanup; } } } else { BOOL bAlphaFound = FALSE; hr = ((LPDIRECT3DTEXTURE9)m_ptexOrig)->GetSurfaceLevel(lwMip, &psurfOrig); ((LPDIRECT3DTEXTURE9)m_ptexOrig)->GetLevelDesc(lwMip, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); goto LCleanup; } // Check if the original has alpha if( FormatContainsAlpha(sd.Format) ) { bAlphaFound = TRUE; } if (m_ptexNew != NULL) { hr = ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetSurfaceLevel(lwMip, &psurfNew); ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetLevelDesc(lwMip, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); goto LCleanup; } // Check if the new has alpha if( FormatContainsAlpha(sd.Format) ) { bAlphaFound = TRUE; } } if( bAlphaFound == FALSE ) { AfxMessageBox(ID_ERROR_NEEDALPHA); goto LCleanup; } } if (!PromptForBmp(&fileName)) goto LCleanup; if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfOrig))) goto LCleanup; if (psurfNew != NULL) { if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfNew))) goto LCleanup; } if (pvolOrig != NULL) { hr = LoadVolumeSliceFromSurface(pvolOrig, lwSlice, psurfOrig); } if (pvolNew != NULL) { hr = LoadVolumeSliceFromSurface(pvolNew, lwSlice, psurfNew); } SetModifiedFlag(TRUE); UpdateAllViews(NULL, 1); LCleanup: ReleasePpo(&psurfOrig); ReleasePpo(&psurfNew); ReleasePpo(&ptexOrig); ReleasePpo(&ptexNew); ReleasePpo(&pvolOrig); ReleasePpo(&pvolNew); }
void CDxtexDoc::OnUpdateFormatChangeCubeMapFaces(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsCubeMap() && !IsVolumeMap()); }
void CDxtexDoc::OpenSubsurface(D3DCUBEMAP_FACES FaceType, LONG lwMip, LONG lwSlice) { HRESULT hr; CString fileName; LPDIRECT3DDEVICE9 pd3ddev = PDxtexApp()->Pd3ddev(); LPDIRECT3DTEXTURE9 ptex = NULL; LPDIRECT3DSURFACE9 psurfOrig = NULL; LPDIRECT3DSURFACE9 psurfNew = NULL; if (!PromptForBmp(&fileName)) return; if (IsVolumeMap()) { hr = D3DXCreateTextureFromFile(pd3ddev, fileName, &ptex); hr = ptex->GetSurfaceLevel(0, &psurfOrig); } else if (IsCubeMap()) { hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexOrig)->GetCubeMapSurface(FaceType, lwMip, &psurfOrig); if (m_ptexNew != NULL) hr = ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetCubeMapSurface(FaceType, lwMip, &psurfNew); hr = D3DXLoadSurfaceFromFile(psurfOrig, NULL, NULL, fileName, NULL, D3DX_DEFAULT, 0, NULL); } else { hr = ((LPDIRECT3DTEXTURE9)m_ptexOrig)->GetSurfaceLevel(lwMip, &psurfOrig); if (m_ptexNew != NULL) hr = ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetSurfaceLevel(lwMip, &psurfNew); hr = D3DXLoadSurfaceFromFile(psurfOrig, NULL, NULL, fileName, NULL, D3DX_DEFAULT, 0, NULL); } // Look for "foo_a.bmp" for alpha channel int i = fileName.ReverseFind('.'); fileName = fileName.Left(i) + "_a.bmp"; CFileStatus status; if (CFile::GetStatus(fileName, status)) { if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurfOrig))) return; } if (IsVolumeMap()) { LPDIRECT3DVOLUME9 pvol; hr = ((LPDIRECT3DVOLUMETEXTURE9)m_ptexOrig)->GetVolumeLevel(lwMip, &pvol); hr = LoadVolumeSliceFromSurface(pvol, lwSlice, psurfOrig); ReleasePpo(&pvol); if (m_ptexNew) { hr = ((LPDIRECT3DVOLUMETEXTURE9)m_ptexNew)->GetVolumeLevel(lwMip, &pvol); hr = LoadVolumeSliceFromSurface(pvol, lwSlice, psurfOrig); ReleasePpo(&pvol); } } else if (psurfNew != NULL) { hr = D3DXLoadSurfaceFromSurface(psurfNew, NULL, NULL, psurfOrig, NULL, NULL, D3DX_DEFAULT, 0); } ReleasePpo(&psurfOrig); ReleasePpo(&psurfNew); ReleasePpo(&ptex); SetModifiedFlag(TRUE); UpdateAllViews(NULL, 1); }
void CDxtexDoc::OnFileOpenAlpha() { HRESULT hr; CString fileName; LPDIRECT3DTEXTURE9 pmiptex; if (IsCubeMap() || IsVolumeMap()) return; // Premultiplied-alpha files don't support this feature: D3DSURFACE_DESC sd; ((LPDIRECT3DTEXTURE9)m_ptexOrig)->GetLevelDesc(0, &sd); if (sd.Format == D3DFMT_DXT2 || sd.Format == D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); return; } // Check if the original has alpha if( !FormatContainsAlpha(sd.Format) ) { // If it doesn't then see if the new does if (m_ptexNew != NULL) { ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetLevelDesc(0, &sd); if( !FormatContainsAlpha(sd.Format) ) { AfxMessageBox(ID_ERROR_NEEDALPHA); return; } else { ReleasePpo(&m_ptexOrig); m_ptexOrig = m_ptexNew; m_ptexNew = NULL; CWnd* Wnd = AfxGetMainWnd(); if( Wnd != NULL ) Wnd->PostMessage(WM_COMMAND, ID_VIEW_ORIGINAL, 0); } } else { AfxMessageBox(ID_ERROR_NEEDALPHA); return; } } pmiptex = (LPDIRECT3DTEXTURE9)m_ptexOrig; if (!PromptForBmp(&fileName)) return; LPDIRECT3DSURFACE9 psurf; if (FAILED(hr = pmiptex->GetSurfaceLevel(0, &psurf))) return; if (FAILED(hr = LoadAlphaIntoSurface(fileName, psurf))) return; if (m_numMips > 1) OnGenerateMipMaps(); else if (m_ptexNew != NULL) { ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetLevelDesc(0, &sd); Compress(sd.Format, FALSE); } UpdateAllViews(NULL, 1); }
void CDxtexDoc::OnUpdateFormatMakeIntoVolumeMap(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsCubeMap() && !IsVolumeMap()); }
bool WebGLTexture::IsComplete(uint32_t texUnit, const char** const out_reason) const { // Texture completeness is established at GLES 3.0.4, p160-161. // "[A] texture is complete unless any of the following conditions hold true:" // "* Any dimension of the `level_base` array is not positive." const ImageInfo& baseImageInfo = BaseImageInfo(); if (!baseImageInfo.IsDefined()) { // In case of undefined texture image, we don't print any message because this is // a very common and often legitimate case (asynchronous texture loading). *out_reason = nullptr; return false; } if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) { *out_reason = "The dimensions of `level_base` are not all positive."; return false; } // "* The texture is a cube map texture, and is not cube complete." if (IsCubeMap() && !IsCubeComplete()) { *out_reason = "Cubemaps must be \"cube complete\"."; return false; } WebGLSampler* sampler = mContext->mBoundSamplers[texUnit]; TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter; TexMagFilter magFilter = sampler ? sampler->mMagFilter : mMagFilter; // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and // the texture is not mipmap complete." const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST && minFilter != LOCAL_GL_LINEAR); if (requiresMipmap && !IsMipmapComplete(texUnit)) { *out_reason = "Because the minification filter requires mipmapping, the texture" " must be \"mipmap complete\"."; return false; } const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST || minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST); const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST); const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest); if (!isFilteringNearestOnly) { auto formatUsage = baseImageInfo.mFormat; auto format = formatUsage->format; // "* The effective internal format specified for the texture arrays is a sized // internal color format that is not texture-filterable, and either the // magnification filter is not NEAREST or the minification filter is neither // NEAREST nor NEAREST_MIPMAP_NEAREST." // Since all (GLES3) unsized color formats are filterable just like their sized // equivalents, we don't have to care whether its sized or not. if (format->isColorFormat && !formatUsage->isFilterable) { *out_reason = "Because minification or magnification filtering is not NEAREST" " or NEAREST_MIPMAP_NEAREST, and the texture's format is a" " color format, its format must be \"texture-filterable\"."; return false; } // "* The effective internal format specified for the texture arrays is a sized // internal depth or depth and stencil format, the value of // TEXTURE_COMPARE_MODE is NONE[1], and either the magnification filter is not // NEAREST, or the minification filter is neither NEAREST nor // NEAREST_MIPMAP_NEAREST." // [1]: This sounds suspect, but is explicitly noted in the change log for GLES // 3.0.1: // "* Clarify that a texture is incomplete if it has a depth component, no // shadow comparison, and linear filtering (also Bug 9481)." // As of OES_packed_depth_stencil rev #3, the sample code explicitly samples from // a DEPTH_STENCIL_OES texture with a min-filter of LINEAR. Therefore we relax // this restriction if WEBGL_depth_texture is enabled. if (!mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { if (format->hasDepth && mTexCompareMode != LOCAL_GL_NONE) { *out_reason = "A depth or depth-stencil format with TEXTURE_COMPARE_MODE" " of NONE must have minification or magnification filtering" " of NEAREST or NEAREST_MIPMAP_NEAREST."; return false; } } } // Texture completeness is effectively (though not explicitly) amended for GLES2 by // the "Texture Access" section under $3.8 "Fragment Shaders". This also applies to // vertex shaders, as noted on GLES 2.0.25, p41. if (!mContext->IsWebGL2()) { // GLES 2.0.25, p87-88: // "Calling a sampler from a fragment shader will return (R,G,B,A)=(0,0,0,1) if // any of the following conditions are true:" // "* A two-dimensional sampler is called, the minification filter is one that // requires a mipmap[...], and the sampler's associated texture object is not // complete[.]" // (already covered) // "* A two-dimensional sampler is called, the minification filter is not one that // requires a mipmap (either NEAREST nor[sic] LINEAR), and either dimension of // the level zero array of the associated texture object is not positive." // (already covered) // "* A two-dimensional sampler is called, the corresponding texture image is a // non-power-of-two image[...], and either the texture wrap mode is not // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR." // "* A cube map sampler is called, any of the corresponding texture images are // non-power-of-two images, and either the texture wrap mode is not // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR." if (!baseImageInfo.IsPowerOfTwo()) { TexWrap wrapS = sampler ? sampler->mWrapS : mWrapS; TexWrap wrapT = sampler ? sampler->mWrapT : mWrapT; // "either the texture wrap mode is not CLAMP_TO_EDGE" if (wrapS != LOCAL_GL_CLAMP_TO_EDGE || wrapT != LOCAL_GL_CLAMP_TO_EDGE) { *out_reason = "Non-power-of-two textures must have a wrap mode of" " CLAMP_TO_EDGE."; return false; } // "or the minification filter is neither NEAREST nor LINEAR" if (requiresMipmap) { *out_reason = "Mipmapping requires power-of-two textures."; return false; } } // "* A cube map sampler is called, and either the corresponding cube map texture // image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a // mipmap and the texture is not mipmap cube complete." // (already covered) } return true; }
void CDxtexDoc::OnUpdateFormatResize(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsCubeMap() && !IsVolumeMap()); }
HRESULT CDxtexDoc::ChangeFormat(LPDIRECT3DBASETEXTURE9 ptexCur, D3DFORMAT fmtTo, LPDIRECT3DBASETEXTURE9* pptexNew) { HRESULT hr; LPDIRECT3DDEVICE9 pd3ddev = PDxtexApp()->Pd3ddev(); LPDIRECT3DTEXTURE9 pmiptex; LPDIRECT3DCUBETEXTURE9 pcubetex; LPDIRECT3DVOLUMETEXTURE9 pvoltex; D3DFORMAT fmtFrom; LPDIRECT3DTEXTURE9 pmiptexNew; LPDIRECT3DCUBETEXTURE9 pcubetexNew; LPDIRECT3DVOLUMETEXTURE9 pvoltexNew; if (IsVolumeMap()) { pvoltex = (LPDIRECT3DVOLUMETEXTURE9)ptexCur; D3DVOLUME_DESC vd; pvoltex->GetLevelDesc(0, &vd); fmtFrom = vd.Format; } else if (IsCubeMap()) { pcubetex = (LPDIRECT3DCUBETEXTURE9)ptexCur; D3DSURFACE_DESC sd; pcubetex->GetLevelDesc(0, &sd); fmtFrom = sd.Format; } else { pmiptex = (LPDIRECT3DTEXTURE9)ptexCur; D3DSURFACE_DESC sd; pmiptex->GetLevelDesc(0, &sd); fmtFrom = sd.Format; } if (fmtFrom == D3DFMT_DXT2 || fmtFrom == D3DFMT_DXT4) { if (fmtTo == D3DFMT_DXT1) { AfxMessageBox(ID_ERROR_PREMULTTODXT1); } else if (fmtTo != D3DFMT_DXT2 && fmtTo != D3DFMT_DXT4) { AfxMessageBox(ID_ERROR_PREMULTALPHA); return S_OK; } } if (IsVolumeMap()) { hr = pd3ddev->CreateVolumeTexture(m_dwWidth, m_dwHeight, m_dwDepth, m_numMips, 0, fmtTo, D3DPOOL_SYSTEMMEM, &pvoltexNew, NULL); if (FAILED(hr)) return hr; *pptexNew = pvoltexNew; if (FAILED(BltAllLevels(D3DCUBEMAP_FACE_FORCE_DWORD, ptexCur, *pptexNew))) return hr; } else if (IsCubeMap()) { hr = pd3ddev->CreateCubeTexture(m_dwWidth, m_numMips, 0, fmtTo, D3DPOOL_MANAGED, &pcubetexNew, NULL); if (FAILED(hr)) return hr; *pptexNew = pcubetexNew; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_NEGATIVE_X, ptexCur, *pptexNew))) return hr; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_POSITIVE_X, ptexCur, *pptexNew))) return hr; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_NEGATIVE_Y, ptexCur, *pptexNew))) return hr; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_POSITIVE_Y, ptexCur, *pptexNew))) return hr; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_NEGATIVE_Z, ptexCur, *pptexNew))) return hr; if (FAILED(hr = BltAllLevels(D3DCUBEMAP_FACE_POSITIVE_Z, ptexCur, *pptexNew))) return hr; } else { if ((fmtTo == D3DFMT_DXT1 || fmtTo == D3DFMT_DXT2 || fmtTo == D3DFMT_DXT3 || fmtTo == D3DFMT_DXT4 || fmtTo == D3DFMT_DXT5) && (m_dwWidth % 4 != 0 || m_dwHeight % 4 != 0)) { AfxMessageBox(ID_ERROR_NEEDMULTOF4); return E_FAIL; } hr = pd3ddev->CreateTexture(m_dwWidth, m_dwHeight, m_numMips, 0, fmtTo, D3DPOOL_MANAGED, &pmiptexNew, NULL); if (FAILED(hr)) return hr; *pptexNew = pmiptexNew; if (FAILED(BltAllLevels(D3DCUBEMAP_FACE_FORCE_DWORD, ptexCur, *pptexNew))) return hr; } return S_OK; }
void CDxtexDoc::GenerateMipMaps() { LONG lwTempH; LONG lwTempW; LONG lwPowsW; LONG lwPowsH; LPDIRECT3DTEXTURE9 pddsNew = NULL; D3DFORMAT fmt; HRESULT hr; LPDIRECT3DDEVICE9 pd3ddev = PDxtexApp()->Pd3ddev(); LPDIRECT3DTEXTURE9 pmiptex = NULL; LPDIRECT3DCUBETEXTURE9 pcubetex = NULL; LPDIRECT3DVOLUMETEXTURE9 pvoltex = NULL; LPDIRECT3DTEXTURE9 pmiptexNew = NULL; LPDIRECT3DCUBETEXTURE9 pcubetexNew = NULL; LPDIRECT3DVOLUMETEXTURE9 pvoltexNew = NULL; LPDIRECT3DSURFACE9 psurfSrc; LPDIRECT3DSURFACE9 psurfDest; LPDIRECT3DVOLUME9 pvolSrc; LPDIRECT3DVOLUME9 pvolDest; if (IsVolumeMap()) pvoltex = (LPDIRECT3DVOLUMETEXTURE9)m_ptexOrig; else if (IsCubeMap()) pcubetex = (LPDIRECT3DCUBETEXTURE9)m_ptexOrig; else pmiptex = (LPDIRECT3DTEXTURE9)m_ptexOrig; if (pvoltex != NULL) { D3DVOLUME_DESC vd; pvoltex->GetLevelDesc(0, &vd); fmt = vd.Format; } else if (pcubetex != NULL) { D3DSURFACE_DESC sd; pcubetex->GetLevelDesc(0, &sd); fmt = sd.Format; } else { D3DSURFACE_DESC sd; pmiptex->GetLevelDesc(0, &sd); fmt = sd.Format; } lwTempW = m_dwWidth; lwTempH = m_dwHeight; lwPowsW = 0; lwPowsH = 0; while (lwTempW > 0) { lwPowsW++; lwTempW = lwTempW / 2; } while (lwTempH > 0) { lwPowsH++; lwTempH = lwTempH / 2; } m_numMips = lwPowsW > lwPowsH ? lwPowsW : lwPowsH; // Create destination mipmap surface - same format as source if (pvoltex != NULL) { if (FAILED(hr = pd3ddev->CreateVolumeTexture(m_dwWidth, m_dwHeight, m_dwDepth, m_numMips, 0, fmt, D3DPOOL_SYSTEMMEM, &pvoltexNew, NULL))) { goto LFail; } hr = pvoltex->GetVolumeLevel(0, &pvolSrc); hr = pvoltexNew->GetVolumeLevel(0, &pvolDest); hr = D3DXLoadVolumeFromVolume(pvolDest, NULL, NULL, pvolSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&pvolSrc); ReleasePpo(&pvolDest); hr = D3DXFilterVolumeTexture(pvoltexNew, NULL, 0, D3DX_DEFAULT); } else if (pmiptex != NULL) { if (FAILED(hr = pd3ddev->CreateTexture(m_dwWidth, m_dwHeight, m_numMips, 0, fmt, D3DPOOL_MANAGED, &pmiptexNew, NULL))) { goto LFail; } hr = pmiptex->GetSurfaceLevel(0, &psurfSrc); hr = pmiptexNew->GetSurfaceLevel(0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = D3DXFilterTexture(pmiptexNew, NULL, 0, D3DX_DEFAULT); } else { if (FAILED(hr = pd3ddev->CreateCubeTexture(m_dwWidth, m_numMips, 0, fmt, D3DPOOL_MANAGED, &pcubetexNew, NULL))) { goto LFail; } hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_X, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_X, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_Y, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_Y, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_Y, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_Y, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_Z, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_Z, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = pcubetex->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_Z, 0, &psurfSrc); hr = pcubetexNew->GetCubeMapSurface(D3DCUBEMAP_FACE_NEGATIVE_Z, 0, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); hr = D3DXFilterCubeTexture(pcubetexNew, NULL, 0, D3DX_DEFAULT); } ReleasePpo(&m_ptexOrig); if (pvoltexNew != NULL) m_ptexOrig = pvoltexNew; else if (pcubetexNew != NULL) m_ptexOrig = pcubetexNew; else m_ptexOrig = pmiptexNew; if (m_ptexNew != NULL) { // Rather than filtering down the (probably-compressed) m_ptexNew // top level, compress each mip level from the (probably-uncompressed) // m_ptexOrig levels. if (pvoltexNew != NULL) { D3DVOLUME_DESC vd; ((LPDIRECT3DVOLUMETEXTURE9)m_ptexNew)->GetLevelDesc(0, &vd); fmt = vd.Format; } else if (pcubetexNew != NULL) { D3DSURFACE_DESC sd; ((LPDIRECT3DTEXTURE9)m_ptexNew)->GetLevelDesc(0, &sd); fmt = sd.Format; } else { D3DSURFACE_DESC sd; ((LPDIRECT3DCUBETEXTURE9)m_ptexNew)->GetLevelDesc(0, &sd); fmt = sd.Format; } Compress(fmt, FALSE); } m_bTitleModsChanged = TRUE; // Generate title bar update UpdateAllViews(NULL, 1); // tell CView to pick up new surface pointers SetModifiedFlag(); return; LFail: ReleasePpo(&pddsNew); }
void WebGLTexture::GenerateMipmap(TexTarget texTarget) { // GLES 3.0.4 p160: // "Mipmap generation replaces texel array levels level base + 1 through q with arrays // derived from the level base array, regardless of their previous contents. All // other mipmap arrays, including the level base array, are left unchanged by this // computation." const ImageInfo& baseImageInfo = BaseImageInfo(); if (!baseImageInfo.IsDefined()) { mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture is" " not defined."); return; } if (IsCubeMap() && !IsCubeComplete()) { mContext->ErrorInvalidOperation("generateMipmap: Cube maps must be \"cube" " complete\"."); return; } if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) { mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture" " does not have power-of-two dimensions."); return; } auto format = baseImageInfo.mFormat->format; if (format->compression) { mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is" " compressed."); return; } if (format->hasDepth) { mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not" " supported."); return; } // OpenGL ES 3.0.4 p160: // If the level base array was not specified with an unsized internal format from // table 3.3 or a sized internal format that is both color-renderable and // texture-filterable according to table 3.13, an INVALID_OPERATION error // is generated. const auto usage = baseImageInfo.mFormat; bool canGenerateMipmap = (usage->isRenderable && usage->isFilterable); switch (usage->format->effectiveFormat) { case webgl::EffectiveFormat::Luminance8: case webgl::EffectiveFormat::Alpha8: case webgl::EffectiveFormat::Luminance8Alpha8: // Non-color-renderable formats from Table 3.3. canGenerateMipmap = true; break; default: break; } if (!canGenerateMipmap) { mContext->ErrorInvalidOperation("generateMipmap: Texture at base level is not unsized" " internal format or is not" " color-renderable or texture-filterable."); return; } // Done with validation. Do the operation. mContext->MakeContextCurrent(); gl::GLContext* gl = mContext->gl; if (gl->WorkAroundDriverBugs()) { // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance // overhead so we do it unconditionally. // // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105. gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST); gl->fGenerateMipmap(texTarget.get()); gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, mMinFilter.get()); } else { gl->fGenerateMipmap(texTarget.get()); } // Record the results. // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns // mBaseMipmapLevel if the min filter doesn't require mipmaps. const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1; PopulateMipChain(mBaseMipmapLevel, lastLevel); }
HRESULT CDxtexDoc::BltAllLevels(D3DCUBEMAP_FACES FaceType, LPDIRECT3DBASETEXTURE9 ptexSrc, LPDIRECT3DBASETEXTURE9 ptexDest) { HRESULT hr; LPDIRECT3DTEXTURE9 pmiptexSrc; LPDIRECT3DTEXTURE9 pmiptexDest; LPDIRECT3DCUBETEXTURE9 pcubetexSrc; LPDIRECT3DCUBETEXTURE9 pcubetexDest; LPDIRECT3DVOLUMETEXTURE9 pvoltexSrc; LPDIRECT3DVOLUMETEXTURE9 pvoltexDest; DWORD iLevel; if (IsVolumeMap()) { pvoltexSrc = (LPDIRECT3DVOLUMETEXTURE9)ptexSrc; pvoltexDest = (LPDIRECT3DVOLUMETEXTURE9)ptexDest; } else if (IsCubeMap()) { pcubetexSrc = (LPDIRECT3DCUBETEXTURE9)ptexSrc; pcubetexDest = (LPDIRECT3DCUBETEXTURE9)ptexDest; } else { pmiptexSrc = (LPDIRECT3DTEXTURE9)ptexSrc; pmiptexDest = (LPDIRECT3DTEXTURE9)ptexDest; } for (iLevel = 0; iLevel < m_numMips; iLevel++) { if (IsVolumeMap()) { LPDIRECT3DVOLUME9 pvolSrc = NULL; LPDIRECT3DVOLUME9 pvolDest = NULL; hr = pvoltexSrc->GetVolumeLevel(iLevel, &pvolSrc); hr = pvoltexDest->GetVolumeLevel(iLevel, &pvolDest); hr = D3DXLoadVolumeFromVolume(pvolDest, NULL, NULL, pvolSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&pvolSrc); ReleasePpo(&pvolDest); } else if (IsCubeMap()) { LPDIRECT3DSURFACE9 psurfSrc = NULL; LPDIRECT3DSURFACE9 psurfDest = NULL; hr = pcubetexSrc->GetCubeMapSurface(FaceType, iLevel, &psurfSrc); hr = pcubetexDest->GetCubeMapSurface(FaceType, iLevel, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); } else { LPDIRECT3DSURFACE9 psurfSrc = NULL; LPDIRECT3DSURFACE9 psurfDest = NULL; hr = pmiptexSrc->GetSurfaceLevel(iLevel, &psurfSrc); hr = pmiptexDest->GetSurfaceLevel(iLevel, &psurfDest); hr = D3DXLoadSurfaceFromSurface(psurfDest, NULL, NULL, psurfSrc, NULL, NULL, D3DX_DEFAULT, 0); ReleasePpo(&psurfSrc); ReleasePpo(&psurfDest); } } return S_OK; }