1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-10 18:51:21 +03:00
occt/src/OpenGl/OpenGl_Texture.cxx
kgv 633084b809 0032862: Visualization, Graphic3d_TextureMap - add 3D texture definition
Image_PixMap has been extended to support definition of 3D bitmap (as an array of 2D slices).

Graphic3d_TypeOfTexture enumeration values have been renamed to include full enum prefix.
Added Graphic3d_TypeOfTexture_3D redirecting to GL_TEXTURE_3D.
OpenGl_Texture::Init() has been extended to allow initialization of 3D texture.

Graphic3d_Texture2Dmanual merged into Graphic3d_Texture2D and marked as deprecated alias.
Graphic3d_TOT_2D_MIPMAP has been deprecated in favor of dedicated Graphic3d_TextureRoot::SetMipMaps().

Added Graphic3d_Texture3D class.
vtexture - added argument -3d for uploading 3D texture.
2022-03-05 17:31:39 +03:00

1403 lines
53 KiB
C++

// Created by: Kirill GAVRILOV
// Copyright (c) 2013-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <OpenGl_Texture.hxx>
#include <OpenGl_ArbFBO.hxx>
#include <OpenGl_Context.hxx>
#include <OpenGl_GlCore45.hxx>
#include <OpenGl_Sampler.hxx>
#include <Graphic3d_TextureParams.hxx>
#include <TCollection_ExtendedString.hxx>
#include <Standard_Assert.hxx>
#include <Image_CompressedPixMap.hxx>
#include <Image_PixMap.hxx>
#include <Image_SupportedFormats.hxx>
#include <algorithm>
IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_NamedResource)
namespace
{
//! Simple class to reset unpack alignment settings
struct OpenGl_UnpackAlignmentSentry
{
//! Reset unpack alignment settings to safe values
static void Reset (const OpenGl_Context& theCtx)
{
theCtx.core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
if (theCtx.hasUnpackRowLength)
{
theCtx.core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
}
}
OpenGl_UnpackAlignmentSentry (const Handle(OpenGl_Context)& theCtx)
: myCtx (theCtx.get()) {}
~OpenGl_UnpackAlignmentSentry()
{
Reset (*myCtx);
}
private:
OpenGl_Context* myCtx;
};
//! Compute the upper mipmap level for complete mipmap set (e.g. till the 1x1 level).
static Standard_Integer computeUpperMipMapLevel (Standard_Integer theSize)
{
for (Standard_Integer aMipIter = 0;; ++aMipIter, theSize /= 2)
{
if (theSize <= 1)
{
return aMipIter;
}
}
}
//! Compute size of the smallest defined mipmap level (for verbose messages).
static Graphic3d_Vec2i computeSmallestMipMapSize (const Graphic3d_Vec2i& theBaseSize, Standard_Integer theMaxLevel)
{
Graphic3d_Vec2i aMipSizeXY = theBaseSize;
for (Standard_Integer aMipIter = 0;; ++aMipIter)
{
if (aMipIter > theMaxLevel)
{
return aMipSizeXY;
}
aMipSizeXY /= 2;
if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
}
}
}
// =======================================================================
// function : OpenGl_Texture
// purpose :
// =======================================================================
OpenGl_Texture::OpenGl_Texture (const TCollection_AsciiString& theResourceId,
const Handle(Graphic3d_TextureParams)& theParams)
: OpenGl_NamedResource (theResourceId),
mySampler (new OpenGl_Sampler (theParams)),
myRevision (0),
myTextureId (NO_TEXTURE),
myTarget (GL_TEXTURE_2D),
myTextFormat (GL_RGBA),
mySizedFormat(GL_RGBA8),
myNbSamples (1),
myMaxMipLevel(0),
myIsAlpha (false),
myIsTopDown (true)
{
//
}
// =======================================================================
// function : ~OpenGl_Texture
// purpose :
// =======================================================================
OpenGl_Texture::~OpenGl_Texture()
{
Release (NULL);
}
// =======================================================================
// function : Create
// purpose :
// =======================================================================
bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& theCtx)
{
if (myTextureId != NO_TEXTURE)
{
return true;
}
theCtx->core11fwd->glGenTextures (1, &myTextureId);
if (myTextureId == NO_TEXTURE)
{
return false;
}
//mySampler->Create (theCtx); // do not create sampler object by default
return true;
}
// =======================================================================
// function : Release
// purpose :
// =======================================================================
void OpenGl_Texture::Release (OpenGl_Context* theGlCtx)
{
mySampler->Release (theGlCtx);
if (myTextureId == NO_TEXTURE)
{
return;
}
// application can not handle this case by exception - this is bug in code
Standard_ASSERT_RETURN (theGlCtx != NULL,
"OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
if (theGlCtx->IsValid())
{
theGlCtx->core11fwd->glDeleteTextures (1, &myTextureId);
}
myTextureId = NO_TEXTURE;
mySize.SetValues (0, 0, 0);
}
// =======================================================================
// function : applyDefaultSamplerParams
// purpose :
// =======================================================================
void OpenGl_Texture::applyDefaultSamplerParams (const Handle(OpenGl_Context)& theCtx)
{
OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), NULL, myTarget, myMaxMipLevel);
if (mySampler->IsValid() && !mySampler->IsImmutable())
{
OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), mySampler.get(), myTarget, myMaxMipLevel);
}
}
// =======================================================================
// function : Bind
// purpose :
// =======================================================================
void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
const Graphic3d_TextureUnit theTextureUnit) const
{
if (theCtx->core15fwd != NULL)
{
theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
}
mySampler->Bind (theCtx, theTextureUnit);
theCtx->core11fwd->glBindTexture (myTarget, myTextureId);
}
// =======================================================================
// function : Unbind
// purpose :
// =======================================================================
void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
const Graphic3d_TextureUnit theTextureUnit) const
{
if (theCtx->core15fwd != NULL)
{
theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
}
mySampler->Unbind (theCtx, theTextureUnit);
theCtx->core11fwd->glBindTexture (myTarget, NO_TEXTURE);
}
//=======================================================================
//function : InitSamplerObject
//purpose :
//=======================================================================
bool OpenGl_Texture::InitSamplerObject (const Handle(OpenGl_Context)& theCtx)
{
return myTextureId != NO_TEXTURE
&& mySampler->Init (theCtx, *this);
}
// =======================================================================
// function : Init
// purpose :
// =======================================================================
bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
const OpenGl_TextureFormat& theFormat,
const Graphic3d_Vec3i& theSizeXYZ,
const Graphic3d_TypeOfTexture theType,
const Image_PixMap* theImage)
{
if (theSizeXYZ.x() < 1
|| theSizeXYZ.y() < 1
|| theSizeXYZ.z() < 1)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: texture of 0 size cannot be created [") + myResourceId +"]");
Release (theCtx.get());
return false;
}
GLenum aTarget = GL_TEXTURE_2D;
switch (theType)
{
case Graphic3d_TypeOfTexture_1D:
{
aTarget = theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
? GL_TEXTURE_1D
: GL_TEXTURE_2D;
break;
}
case Graphic3d_TypeOfTexture_2D:
case Graphic3d_TOT_2D_MIPMAP:
{
aTarget = GL_TEXTURE_2D;
break;
}
case Graphic3d_TypeOfTexture_3D:
{
aTarget = GL_TEXTURE_3D;
break;
}
case Graphic3d_TypeOfTexture_CUBEMAP:
{
aTarget = GL_TEXTURE_CUBE_MAP;
break;
}
}
const bool toPatchExisting = IsValid()
&& myTextFormat == theFormat.PixelFormat()
&& myTarget == aTarget
&& mySize.x() == theSizeXYZ.x()
&& (mySize.y() == theSizeXYZ.y() || theType == Graphic3d_TypeOfTexture_1D)
&& mySize.z() == theSizeXYZ.z();
if (!Create (theCtx))
{
Release (theCtx.get());
return false;
}
if (theImage != NULL)
{
myIsAlpha = theImage->Format() == Image_Format_Alpha
|| theImage->Format() == Image_Format_AlphaF;
myIsTopDown = theImage->IsTopDown();
}
else
{
myIsAlpha = theFormat.PixelFormat() == GL_ALPHA;
}
myMaxMipLevel = 0;
myTextFormat = theFormat.PixelFormat();
mySizedFormat = theFormat.InternalFormat();
myNbSamples = 1;
// ES 2.0 does not support sized formats and format conversions - them detected from data type
const GLint anIntFormat = (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
|| theCtx->IsGlGreaterEqual (3, 0))
? theFormat.InternalFormat()
: theFormat.PixelFormat();
if (theFormat.DataType() == GL_FLOAT
&& !theCtx->arbTexFloat)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: floating-point textures are not supported by hardware [") + myResourceId +"]");
Release (theCtx.get());
return false;
}
const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
if (theSizeXYZ.maxComp() > aMaxSize)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: Texture dimension - ") + theSizeXYZ.x() + "x" + theSizeXYZ.y()
+ (theSizeXYZ.z() > 1 ? TCollection_AsciiString ("x") + theSizeXYZ.z() : TCollection_AsciiString())
+ " exceeds hardware limits (" + aMaxSize + "x" + aMaxSize + ")"
+ " [" + myResourceId +"]");
Release (theCtx.get());
return false;
}
else if (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGL
&& !theCtx->IsGlGreaterEqual (3, 0)
&& !theCtx->arbNPTW)
{
// Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
// however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
// Trying to create NPOT textures on such hardware will not fail
// but driver will fall back into software rendering,
const Graphic3d_Vec2i aSizeP2 (OpenGl_Context::GetPowerOfTwo (theSizeXYZ.x(), aMaxSize),
OpenGl_Context::GetPowerOfTwo (theSizeXYZ.y(), aMaxSize));
if (theSizeXYZ.x() != aSizeP2.x()
|| (theType != Graphic3d_TypeOfTexture_1D && theSizeXYZ.y() != aSizeP2.y()))
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: NPOT Textures (") + theSizeXYZ.x() + "x" + theSizeXYZ.y() + ")"
" are not supported by hardware [" + myResourceId +"]");
Release (theCtx.get());
return false;
}
}
GLint aTestWidth = 0, aTestHeight = 0;
GLvoid* aDataPtr = (theImage != NULL) ? (GLvoid* )theImage->Data() : NULL;
// setup the alignment
OpenGl_UnpackAlignmentSentry anUnpackSentry (theCtx);
(void)anUnpackSentry; // avoid compiler warning
if (aDataPtr != NULL)
{
const GLint anAligment = Min ((GLint )theImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
const GLint anExtraBytes = GLint(theImage->RowExtraBytes());
const GLint aPixelsWidth = GLint(theImage->SizeRowBytes() / theImage->SizePixelBytes());
if (theCtx->hasUnpackRowLength)
{
theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, (anExtraBytes >= anAligment) ? aPixelsWidth : 0);
}
else if (anExtraBytes >= anAligment)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: unsupported image stride within OpenGL ES 2.0 [") + myResourceId +"]");
Release (theCtx.get());
return false;
}
}
myTarget = aTarget;
switch (theType)
{
case Graphic3d_TypeOfTexture_1D:
{
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ( "Error: 1D textures are not supported by hardware [") + myResourceId +"]");
Release (theCtx.get());
return false;
}
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
if (toPatchExisting)
{
theCtx->core11fwd->glTexSubImage1D (GL_TEXTURE_1D, 0, 0,
theSizeXYZ.x(), theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
break;
}
// use proxy to check texture could be created or not
theCtx->core11fwd->glTexImage1D (GL_PROXY_TEXTURE_1D, 0, anIntFormat,
theSizeXYZ.x(), 0,
theFormat.PixelFormat(), theFormat.DataType(), NULL);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
if (aTestWidth == 0)
{
// no memory or broken input parameters
Unbind (theCtx);
Release (theCtx.operator->());
return false;
}
theCtx->core11fwd->glTexImage1D (GL_TEXTURE_1D, 0, anIntFormat,
theSizeXYZ.x(), 0,
theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
{
Unbind (theCtx);
Release (theCtx.get());
return false;
}
mySize.SetValues (theSizeXYZ.x(), 1, 1);
break;
}
case Graphic3d_TypeOfTexture_2D:
case Graphic3d_TOT_2D_MIPMAP:
{
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
if (toPatchExisting)
{
theCtx->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0,
0, 0,
theSizeXYZ.x(), theSizeXYZ.y(),
theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
break;
}
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
{
// use proxy to check texture could be created or not
theCtx->core11fwd->glTexImage2D (GL_PROXY_TEXTURE_2D, 0, anIntFormat,
theSizeXYZ.x(), theSizeXYZ.y(), 0,
theFormat.PixelFormat(), theFormat.DataType(), NULL);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
if (aTestWidth == 0 || aTestHeight == 0)
{
// no memory or broken input parameters
Unbind (theCtx);
Release (theCtx.get());
return false;
}
}
theCtx->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, anIntFormat,
theSizeXYZ.x(), theSizeXYZ.y(), 0,
theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
GLenum anErr = theCtx->core11fwd->glGetError();
if (anErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: 2D texture ") + theSizeXYZ.x() + "x" + theSizeXYZ.y()
+ " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
+ " PF: " + OpenGl_TextureFormat::FormatFormat (theFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (theFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (anErr)
+ " [" + myResourceId +"]");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
mySize.SetValues (theSizeXYZ.xy(), 1);
break;
}
case Graphic3d_TypeOfTexture_3D:
{
if (theCtx->Functions()->glTexImage3D == nullptr)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: three-dimensional textures are not supported by hardware.");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
{
theCtx->Functions()->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, anIntFormat,
theSizeXYZ.x(), theSizeXYZ.y(), theSizeXYZ.z(), 0,
theFormat.PixelFormat(), theFormat.DataType(), nullptr);
NCollection_Vec3<GLint> aTestSizeXYZ;
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &aTestSizeXYZ.x());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &aTestSizeXYZ.z());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
{
Unbind (theCtx);
Release (theCtx.get());
return false;
}
}
theCtx->Functions()->glTexImage3D (GL_TEXTURE_3D, 0, anIntFormat,
theSizeXYZ.x(), theSizeXYZ.y(), theSizeXYZ.z(), 0,
theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
GLenum anErr = theCtx->core11fwd->glGetError();
if (anErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: 3D texture ") + theSizeXYZ.x() + "x" + theSizeXYZ.y() + "x" + theSizeXYZ.z()
+ " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
+ " PF: " + OpenGl_TextureFormat::FormatFormat (theFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (theFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (anErr)
+ " [" + myResourceId +"]");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
mySize = theSizeXYZ;
break;
}
case Graphic3d_TypeOfTexture_CUBEMAP:
{
Unbind (theCtx);
Release (theCtx.get());
return false;
}
}
Unbind (theCtx);
return true;
}
// =======================================================================
// function : GenerateMipmaps
// purpose :
// =======================================================================
bool OpenGl_Texture::GenerateMipmaps (const Handle(OpenGl_Context)& theCtx)
{
if (theCtx->arbFBO == nullptr
|| !IsValid())
{
return false;
}
myMaxMipLevel = computeUpperMipMapLevel (mySize.maxComp());
const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
&& !theCtx->IsGlGreaterEqual (3, 0))
{
// Mipmap NPOT textures are not supported by OpenGL ES 2.0.
const Graphic3d_Vec2i aSizeP2 (OpenGl_Context::GetPowerOfTwo (mySize.x(), aMaxSize),
OpenGl_Context::GetPowerOfTwo (mySize.y(), aMaxSize));
if (mySize.xy() != aSizeP2)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Warning: Mipmap NPOT Textures (") + mySize.x() + "x" + mySize.y() + ")"
" are not supported by OpenGL ES 2.0 [" + myResourceId +"]");
myMaxMipLevel = 0;
}
}
if (myMaxMipLevel <= 0)
{
return false;
}
//glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
Bind (theCtx);
if (theCtx->HasTextureBaseLevel()
&& !mySampler->isValidSampler())
{
const Standard_Integer aMaxLevel = Min (myMaxMipLevel, mySampler->Parameters()->MaxLevel());
mySampler->SetParameter (theCtx, myTarget, GL_TEXTURE_MAX_LEVEL, aMaxLevel);
}
theCtx->arbFBO->glGenerateMipmap (myTarget);
GLenum anErr = theCtx->core11fwd->glGetError();
if (anErr != GL_NO_ERROR)
{
myMaxMipLevel = 0;
if (theCtx->HasTextureBaseLevel()
&& !mySampler->isValidSampler())
{
mySampler->SetParameter (theCtx, myTarget, GL_TEXTURE_MAX_LEVEL, 0);
}
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
&& (mySizedFormat == GL_RGB8
|| mySizedFormat == GL_SRGB8))
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Warning: generating mipmaps requires color-renderable format, while giving ")
+ OpenGl_TextureFormat::FormatFormat (mySizedFormat) + " [" + myResourceId +"]");
}
else
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Warning: generating mipmaps has failed [") + myResourceId +"]");
}
}
applyDefaultSamplerParams (theCtx);
Unbind (theCtx);
return true;
}
// =======================================================================
// function : Init
// purpose :
// =======================================================================
bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
const Image_PixMap& theImage,
const Graphic3d_TypeOfTexture theType,
const Standard_Boolean theIsColorMap)
{
if (theImage.IsEmpty())
{
Release (theCtx.get());
return false;
}
const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theImage.Format(), theIsColorMap);
if (!aFormat.IsValid())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theImage.Format()) + " image format"
+ " [" + myResourceId +"]");
Release (theCtx.get());
return false;
}
return Init (theCtx, aFormat, Graphic3d_Vec3i (theImage.SizeXYZ()), theType, &theImage);
}
// =======================================================================
// function : Init
// purpose :
// =======================================================================
bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
const Handle(Graphic3d_TextureRoot)& theTextureMap)
{
if (theTextureMap.IsNull())
{
return false;
}
switch (theTextureMap->Type())
{
case Graphic3d_TypeOfTexture_CUBEMAP:
{
return InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)::DownCast(theTextureMap),
0, Image_Format_RGB, false, theTextureMap->IsColorMap());
}
default:
{
if (theCtx->SupportedTextureFormats()->HasCompressed()
&& !theCtx->caps->compressedTexturesDisable)
{
if (Handle(Image_CompressedPixMap) aCompressed = theTextureMap->GetCompressedImage (theCtx->SupportedTextureFormats()))
{
return InitCompressed (theCtx, *aCompressed, theTextureMap->IsColorMap());
}
}
Handle(Image_PixMap) anImage = theTextureMap->GetImage (theCtx->SupportedTextureFormats());
if (anImage.IsNull())
{
return false;
}
if (!Init (theCtx, *anImage, theTextureMap->Type(), theTextureMap->IsColorMap()))
{
return false;
}
if (theTextureMap->HasMipmaps())
{
GenerateMipmaps (theCtx);
}
return true;
}
}
}
// =======================================================================
// function : InitCompressed
// purpose :
// =======================================================================
bool OpenGl_Texture::InitCompressed (const Handle(OpenGl_Context)& theCtx,
const Image_CompressedPixMap& theImage,
const Standard_Boolean theIsColorMap)
{
if (theImage.SizeX() < 1
|| theImage.SizeY() < 1
|| theImage.FaceData().IsNull())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: texture of 0 size cannot be created.");
Release (theCtx.get());
return false;
}
if (theImage.SizeX() > theCtx->MaxTextureSize()
|| theImage.SizeY() > theCtx->MaxTextureSize())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: Texture dimension - ") + theImage.SizeX() + "x" + theImage.SizeY()
+ " exceeds hardware limits (" + theCtx->MaxTextureSize() + "x" + theCtx->MaxTextureSize() + ")");
Release (theCtx.get());
return false;
}
const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, theImage.CompressedFormat(), theIsColorMap);
if (!aFormat.IsValid())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theImage.CompressedFormat()) + " image format "
+ " [" + myResourceId +"]");
Release (theCtx.get());
return false;
}
if (!Create (theCtx))
{
return false;
}
myTarget = GL_TEXTURE_2D;
myNbSamples = 1;
myTextFormat = aFormat.Format();
mySizedFormat = aFormat.Internal();
myIsTopDown = theImage.IsTopDown();
mySize.SetValues (theImage.SizeX(), theImage.SizeY(), 1);
myMaxMipLevel = Max (theImage.MipMaps().Size() - 1, 0);
if (myMaxMipLevel > 0
&& !theImage.IsCompleteMipMapSet())
{
const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (mySize.xy(), myMaxMipLevel);
if (!theCtx->HasTextureBaseLevel())
{
myMaxMipLevel = 0;
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySize.x() + "x" + mySize.y()
+ " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
}
else
{
Message::SendTrace (TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySize.x() + "x" + mySize.y()
+ " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
}
}
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
// setup the alignment
OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
Graphic3d_Vec2i aMipSizeXY (theImage.SizeX(), theImage.SizeY());
const Standard_Byte* aData = theImage.FaceData()->Data();
for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
{
const Standard_Integer aMipLength = theImage.MipMaps().Value (aMipIter);
theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_2D, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
if (aTexImgErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: 2D compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
+ " IF: " + OpenGl_TextureFormat::FormatFormat (aFormat.Internal())
+ " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
aData += aMipLength;
aMipSizeXY /= 2;
if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
}
Unbind (theCtx);
return true;
}
// =======================================================================
// function : Init2DMultisample
// purpose :
// =======================================================================
bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
const Standard_Integer theNbSamples,
const Standard_Integer theTextFormat,
const Standard_Integer theSizeX,
const Standard_Integer theSizeY)
{
if (!Create (theCtx)
|| theNbSamples > theCtx->MaxMsaaSamples()
|| theNbSamples < 1)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
+ " exceeds samples limit: " + theCtx->MaxMsaaSamples() + ".");
return false;
}
myNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
myTarget = GL_TEXTURE_2D_MULTISAMPLE;
myMaxMipLevel = 0;
if(theSizeX > theCtx->MaxTextureSize()
|| theSizeY > theCtx->MaxTextureSize())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
+ " exceeds size limit: " + theCtx->MaxTextureSize() + "x" + theCtx->MaxTextureSize() + ".");
return false;
}
Bind (theCtx);
//myTextFormat = theTextFormat;
mySizedFormat = theTextFormat;
if (theCtx->HasTextureMultisampling()
&& theCtx->Functions()->glTexStorage2DMultisample != NULL) // OpenGL 4.3
{
theCtx->Functions()->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
}
else if (theCtx->HasTextureMultisampling()
&& theCtx->Functions()->glTexImage2DMultisample != NULL) // OpenGL 3.2
{
theCtx->Functions()->glTexImage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
}
else
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: MSAA textures are not supported by hardware.");
Unbind (theCtx);
return false;
}
const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
if (aTexImgErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
+ " IF: " + OpenGl_TextureFormat::FormatFormat (theTextFormat)
+ " cannot be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
Unbind (theCtx);
return false;
}
mySize.SetValues (theSizeX, theSizeY, 1);
Unbind (theCtx);
return true;
}
// =======================================================================
// function : InitRectangle
// purpose :
// =======================================================================
bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
const Standard_Integer theSizeX,
const Standard_Integer theSizeY,
const OpenGl_TextureFormat& theFormat)
{
if (!theCtx->IsGlGreaterEqual (3, 0)
|| !Create (theCtx)
|| theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES)
{
return false;
}
myTarget = GL_TEXTURE_RECTANGLE;
myNbSamples = 1;
myMaxMipLevel = 0;
const GLsizei aSizeX = Min (theCtx->MaxTextureSize(), theSizeX);
const GLsizei aSizeY = Min (theCtx->MaxTextureSize(), theSizeY);
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
myTextFormat = theFormat.Format();
mySizedFormat = theFormat.Internal();
// setup the alignment
OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
theCtx->core11fwd->glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE, 0, mySizedFormat,
aSizeX, aSizeY, 0,
myTextFormat, GL_FLOAT, NULL);
GLint aTestSizeX = 0, aTestSizeY = 0;
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH, &aTestSizeX);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
if (aTestSizeX == 0 || aTestSizeY == 0)
{
Unbind (theCtx);
return false;
}
theCtx->core11fwd->glTexImage2D (myTarget, 0, mySizedFormat,
aSizeX, aSizeY, 0,
myTextFormat, GL_FLOAT, NULL);
if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
{
Unbind (theCtx);
return false;
}
mySize.SetValues (aSizeX, aSizeY, 1);
Unbind (theCtx);
return true;
}
// =======================================================================
// function : Init3D
// purpose :
// =======================================================================
bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
const OpenGl_TextureFormat& theFormat,
const Graphic3d_Vec3i& theSizeXYZ,
const void* thePixels)
{
if (theCtx->Functions()->glTexImage3D == NULL)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: three-dimensional textures are not supported by hardware.");
return false;
}
if (!Create(theCtx))
{
return false;
}
myTarget = GL_TEXTURE_3D;
myNbSamples = 1;
myMaxMipLevel = 0;
const Graphic3d_Vec3i aSizeXYZ = theSizeXYZ.cwiseMin (Graphic3d_Vec3i (theCtx->MaxTextureSize()));
if (aSizeXYZ != theSizeXYZ)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: 3D texture dimensions exceed hardware limits.");
Release (theCtx.get());
Unbind (theCtx);
return false;
}
Bind (theCtx);
if (theFormat.DataType() == GL_FLOAT
&& !theCtx->arbTexFloat)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: floating-point textures are not supported by hardware.");
Release (theCtx.get());
Unbind (theCtx);
return false;
}
mySizedFormat = theFormat.InternalFormat();
// setup the alignment
OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
{
theCtx->Functions()->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, mySizedFormat,
aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
theFormat.PixelFormat(), theFormat.DataType(), NULL);
NCollection_Vec3<GLint> aTestSizeXYZ;
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &aTestSizeXYZ.x());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &aTestSizeXYZ.z());
theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
{
Unbind (theCtx);
Release (theCtx.get());
return false;
}
}
applyDefaultSamplerParams (theCtx);
theCtx->Functions()->glTexImage3D (myTarget, 0, mySizedFormat,
aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
theFormat.PixelFormat(), theFormat.DataType(), thePixels);
if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
{
Unbind (theCtx);
Release (theCtx.get());
return false;
}
mySize = aSizeXYZ;
Unbind (theCtx);
return true;
}
// =======================================================================
// function : InitCubeMap
// purpose :
// =======================================================================
bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)& theCtx,
const Handle(Graphic3d_CubeMap)& theCubeMap,
Standard_Size theSize,
Image_Format theFormat,
Standard_Boolean theToGenMipmap,
Standard_Boolean theIsColorMap)
{
if (!Create (theCtx))
{
Release (theCtx.get());
return false;
}
Handle(Image_PixMap) anImage;
Handle(Image_CompressedPixMap) aCompImage;
OpenGl_TextureFormat aFormat;
myMaxMipLevel = 0;
if (!theCubeMap.IsNull())
{
theCubeMap->Reset();
if (theCtx->SupportedTextureFormats()->HasCompressed()
&& !theCtx->caps->compressedTexturesDisable)
{
aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
}
if (!aCompImage.IsNull())
{
aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, aCompImage->CompressedFormat(), theIsColorMap);
if (aFormat.IsValid())
{
theToGenMipmap = false;
theSize = aCompImage->SizeX();
theFormat = aCompImage->BaseFormat();
myMaxMipLevel = Max (aCompImage->MipMaps().Size() - 1, 0);
if (myMaxMipLevel > 0
&& !aCompImage->IsCompleteMipMapSet())
{
const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (Graphic3d_Vec2i (aCompImage->SizeX(), aCompImage->SizeY()), myMaxMipLevel);
if (!theCtx->HasTextureBaseLevel())
{
myMaxMipLevel = 0;
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
+ " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
}
else
{
Message::SendTrace (TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
+ " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
}
}
OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
}
else
{
aCompImage.Nullify();
}
}
if (!aFormat.IsValid())
{
anImage = theCubeMap->Reset().Value (theCtx->SupportedTextureFormats());
if (anImage.IsNull())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Unable to get the first side of cubemap");
Release(theCtx.get());
return false;
}
theSize = anImage->SizeX();
theFormat = anImage->Format();
theToGenMipmap = theCubeMap->HasMipmaps();
}
myIsTopDown = theCubeMap->IsTopDown();
}
if (!aFormat.IsValid())
{
aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theFormat, theIsColorMap);
}
if (!aFormat.IsValid())
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theFormat) + " image format"
+ " [" + myResourceId +"]");
Unbind(theCtx);
Release(theCtx.get());
return false;
}
if (theToGenMipmap
&& theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
&& !theCtx->IsGlGreaterEqual (3, 0)
&& (aFormat.PixelFormat() == GL_SRGB_EXT
|| aFormat.PixelFormat() == GL_SRGB_ALPHA_EXT))
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Warning, GL_EXT_sRGB disallows generation of mipmaps - fallback using non-sRGB format")
+ " [" + myResourceId +"]");
aFormat.SetPixelFormat (aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB : GL_RGBA);
aFormat.SetInternalFormat(aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB8 : GL_RGBA8);
}
myTarget = GL_TEXTURE_CUBE_MAP;
myNbSamples = 1;
mySize.SetValues ((GLsizei )theSize, (GLsizei )theSize, 1);
myTextFormat = aFormat.Format();
mySizedFormat = aFormat.Internal();
// ES 2.0 does not support sized formats and format conversions - them detected from data type
const GLint anIntFormat = (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
|| theCtx->IsGlGreaterEqual (3, 0))
? aFormat.InternalFormat()
: aFormat.PixelFormat();
Bind (theCtx);
applyDefaultSamplerParams (theCtx);
for (Standard_Integer i = 0; i < 6; ++i)
{
const Standard_Byte* aData = NULL;
if (!theCubeMap.IsNull())
{
if (i != 0)
{
if (!aCompImage.IsNull())
{
aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
}
else
{
anImage = theCubeMap->Value (theCtx->SupportedTextureFormats());
}
}
if (!aCompImage.IsNull())
{
Graphic3d_Vec2i aMipSizeXY = mySize.xy();
aData = aCompImage->FaceData()->Data();
for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
{
const Standard_Integer aMipLength = aCompImage->MipMaps().Value (aMipIter);
theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
if (aTexImgErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: cubemap compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
+ " IF: " + OpenGl_TextureFormat::FormatFormat (aFormat.Internal())
+ " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
aData += aMipLength;
aMipSizeXY /= 2;
if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
}
theCubeMap->Next();
continue;
}
if (!anImage.IsNull())
{
const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
const GLint anExtraBytes = GLint(anImage->RowExtraBytes());
const GLint aPixelsWidth = GLint(anImage->SizeRowBytes() / anImage->SizePixelBytes());
const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
if (theCtx->hasUnpackRowLength)
{
theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
}
if (aRowLength > 0
&& !theCtx->hasUnpackRowLength)
{
Handle(Image_PixMap) aCopyImage = new Image_PixMap();
aCopyImage->InitTrash (theFormat, theSize, theSize);
const Standard_Size aRowBytesPacked = std::min (aCopyImage->SizeRowBytes(), anImage->SizeRowBytes());
for (unsigned int y = 0; y < theSize; ++y)
{
memcpy (aCopyImage->ChangeRow (y), anImage->ChangeRow (y), aRowBytesPacked);
}
anImage = aCopyImage;
const GLint anAligment2 = Min((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment2);
}
else
{
theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
}
aData = anImage->Data();
}
else
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString() + "Unable to get [" + i + "] side of cubemap");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
theCubeMap->Next();
}
theCtx->core11fwd->glTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
anIntFormat,
GLsizei(theSize), GLsizei(theSize),
0, aFormat.PixelFormat(), aFormat.DataType(),
aData);
OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
const GLenum anErr = theCtx->core11fwd->glGetError();
if (anErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Error: cubemap side ") + (int )theSize + "x" + (int )theSize
+ " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
+ " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (anErr) + ".");
Unbind (theCtx);
Release (theCtx.get());
return false;
}
}
if (theToGenMipmap && theCtx->arbFBO != NULL)
{
GenerateMipmaps (theCtx);
}
Unbind (theCtx.get());
return true;
}
// =======================================================================
// function : PixelSizeOfPixelFormat
// purpose :
// =======================================================================
Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
{
switch(theInternalFormat)
{
// RED variations (GL_RED, OpenGL 3.0+)
case GL_RED:
case GL_R8: return 1;
case GL_R16: return 2;
case GL_R16F: return 2;
case GL_R32F: return 4;
// RGB variations
case GL_RGB: return 3;
case GL_RGB8: return 3;
case GL_RGB16: return 6;
case GL_RGB16F: return 6;
case GL_RGB32F: return 12;
// RGBA variations
case GL_RGBA: return 4;
case GL_RGBA8: return 4;
case GL_RGB10_A2: return 4;
case GL_RGBA12: return 6;
case GL_RGBA16: return 8;
case GL_RGBA16F: return 8;
case GL_RGBA32F: return 16;
//
case GL_BGRA_EXT: return 4;
// ALPHA variations (deprecated)
case GL_ALPHA:
case GL_ALPHA8: return 1;
case GL_ALPHA16: return 2;
case GL_LUMINANCE: return 1;
case GL_LUMINANCE_ALPHA: return 2;
// depth-stencil
case GL_DEPTH24_STENCIL8: return 4;
case GL_DEPTH32F_STENCIL8: return 8;
case GL_DEPTH_COMPONENT16: return 2;
case GL_DEPTH_COMPONENT24: return 3;
case GL_DEPTH_COMPONENT32F: return 4;
// compressed
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // DXT1 uses circa half a byte per pixel (64 bits per 4x4 block)
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // DXT3/5 uses circa 1 byte per pixel (128 bits per 4x4 block)
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
return 1;
}
return 1;
}
// =======================================================================
// function : EstimatedDataSize
// purpose :
// =======================================================================
Standard_Size OpenGl_Texture::EstimatedDataSize() const
{
if (!IsValid())
{
return 0;
}
Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySize.x() * myNbSamples;
if (mySize.y() != 0)
{
aSize *= Standard_Size(mySize.y());
}
if (mySize.z() != 0)
{
aSize *= Standard_Size(mySize.z());
}
if (myTarget == GL_TEXTURE_CUBE_MAP)
{
aSize *= 6; // cube sides
}
if (myMaxMipLevel > 0)
{
aSize = aSize + aSize / 3;
}
return aSize;
}
// =======================================================================
// function : ImageDump
// purpose :
// =======================================================================
bool OpenGl_Texture::ImageDump (Image_PixMap& theImage,
const Handle(OpenGl_Context)& theCtx,
Graphic3d_TextureUnit theTexUnit,
Standard_Integer theLevel,
Standard_Integer theCubeSide) const
{
const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, mySizedFormat);
if (theCtx.IsNull()
|| !IsValid()
|| theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES // glGetTexImage() is unavailable in OpenGL ES
|| theLevel < 0
|| !aFormat.IsValid()
|| aFormat.ImageFormat() == Image_Format_UNKNOWN
|| (myTarget == GL_TEXTURE_CUBE_MAP
&& (theCubeSide < 0 || theCubeSide > 5)))
{
return false;
}
GLenum aTarget = myTarget;
Graphic3d_Vec2i aSize = mySize.xy();
if (myTarget == GL_TEXTURE_CUBE_MAP)
{
aTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + theCubeSide;
}
for (Standard_Integer aMipIter = 0; aMipIter < theLevel; ++aMipIter)
{
aSize /= 2;
if (aSize.x() == 0) { aSize.x() = 1; }
if (aSize.y() == 0) { aSize.y() = 1; }
}
if (!theImage.InitTrash (aFormat.ImageFormat(), aSize.x(), aSize.y()))
{
return false;
}
const GLint anAligment = Min (GLint(theImage.MaxRowAligmentBytes()), 8); // limit to 8 bytes for OpenGL
theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, anAligment);
if (theCtx->hasPackRowLength)
{
theCtx->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
}
// glGetTextureImage() allows avoiding to binding texture id, but apparently requires clean FBO binding state...
//if (theCtx->core45 != NULL) { theCtx->core45->glGetTextureImage (myTextureId, theLevel, aFormat.PixelFormat(), aFormat.DataType(), (GLsizei )theImage.SizeBytes(), theImage.ChangeData()); } else
{
Bind (theCtx, theTexUnit);
theCtx->core11fwd->glGetTexImage (aTarget, theLevel, aFormat.PixelFormat(), aFormat.DataType(), theImage.ChangeData());
Unbind (theCtx, theTexUnit);
}
if (theImage.Format() != aFormat.ImageFormat())
{
Image_PixMap::SwapRgbaBgra (theImage);
}
const bool hasErrors = theCtx->ResetErrors (true);
theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1);
return !hasErrors;
}