aboutsummaryrefslogtreecommitdiff
path: root/libs/dds_header/src/DDSPreview.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/dds_header/src/DDSPreview.py')
-rw-r--r--libs/dds_header/src/DDSPreview.py528
1 files changed, 0 insertions, 528 deletions
diff --git a/libs/dds_header/src/DDSPreview.py b/libs/dds_header/src/DDSPreview.py
deleted file mode 100644
index 78ffffc..0000000
--- a/libs/dds_header/src/DDSPreview.py
+++ /dev/null
@@ -1,528 +0,0 @@
-import struct
-import sys
-import threading
-import enum
-
-from PyQt6.QtCore import QCoreApplication, qDebug, Qt, QSize
-from PyQt6.QtGui import QColor, QOpenGLContext, QSurfaceFormat, QMatrix4x4, QVector4D
-from PyQt6.QtOpenGLWidgets import QOpenGLWidget
-from PyQt6.QtWidgets import QGridLayout, QLabel, QPushButton, QWidget, QColorDialog, QComboBox
-from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLDebugLogger, QOpenGLShader, QOpenGLShaderProgram, QOpenGLTexture, \
- QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLFunctions_4_1_Core, QOpenGLVersionFunctionsFactory
-
-from DDS.DDSFile import DDSFile
-
-if "mobase" not in sys.modules:
- import mobase
-
-vertexShader2D = """
-#version 150
-
-uniform float aspectRatioRatio;
-
-in vec4 position;
-in vec2 texCoordIn;
-
-out vec2 texCoord;
-
-void main()
-{
- texCoord = texCoordIn;
- gl_Position = position;
- if (aspectRatioRatio >= 1.0)
- gl_Position.y /= aspectRatioRatio;
- else
- gl_Position.x *= aspectRatioRatio;
-}
-"""
-
-vertexShaderCube = """
-#version 150
-
-uniform float aspectRatioRatio;
-
-in vec4 position;
-in vec2 texCoordIn;
-
-out vec2 texCoord;
-
-void main()
-{
- texCoord = texCoordIn;
- gl_Position = position;
-}
-"""
-
-fragmentShaderFloat = """
-#version 150
-
-uniform sampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderUInt = """
-#version 150
-
-uniform usampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- // autofilled alpha is 1, so if we have a scaling factor, we need separate ones for luminance and alpha
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderSInt = """
-#version 150
-
-uniform isampler2D aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-void main()
-{
- // autofilled alpha is 1, so if we have a scaling factor and offset, we need separate ones for luminance and alpha
- gl_FragData[0] = channelMatrix * texture(aTexture, texCoord) + channelOffset;
-}
-"""
-
-fragmentShaderCube = """
-#version 150
-
-uniform samplerCube aTexture;
-uniform mat4 channelMatrix;
-uniform vec4 channelOffset;
-
-in vec2 texCoord;
-
-const float PI = 3.1415926535897932384626433832795;
-
-void main()
-{
- float theta = -2.0 * PI * texCoord.x;
- float phi = PI * texCoord.y;
- gl_FragData[0] = channelMatrix * texture(aTexture, vec3(sin(theta) * sin(phi), cos(theta) * sin(phi), cos(phi))) + channelOffset;
-}
-"""
-
-transparencyVS = """
-#version 150
-
-in vec4 position;
-
-void main()
-{
- gl_Position = position;
-}
-"""
-
-transparencyFS = """
-#version 150
-
-uniform vec4 backgroundColour;
-
-void main()
-{
- float x = gl_FragCoord.x;
- float y = gl_FragCoord.y;
- x = mod(x, 16.0);
- y = mod(y, 16.0);
- gl_FragData[0] = x < 8.0 ^^ y < 8.0 ? vec4(vec3(191.0/255.0), 1.0) : vec4(1.0);
- gl_FragData[0].rgb = backgroundColour.rgb * backgroundColour.a + gl_FragData[0].rgb * (1.0 - backgroundColour.a);
-}
-"""
-
-vertices = [
- # vertex coordinates texture coordinates
- -1.0, -1.0, 0.5, 1.0, 0.0, 1.0,
- -1.0, 1.0, 0.5, 1.0, 0.0, 0.0,
- 1.0, 1.0, 0.5, 1.0, 1.0, 0.0,
-
- -1.0, -1.0, 0.5, 1.0, 0.0, 1.0,
- 1.0, 1.0, 0.5, 1.0, 1.0, 0.0,
- 1.0, -1.0, 0.5, 1.0, 1.0, 1.0,
-]
-
-
-class DDSOptions:
- def __init__(self, colour: QColor = QColor(0, 0, 0, 0), channelMatrix: QMatrix4x4 = QMatrix4x4(),
- channelOffset: QVector4D = QVector4D()):
- # QMatrix4x4 with no arguments is the identity matrix, so no channel transformations
- # declare member variables with None values
- self.backgroundColour = None
- self.channelMatrix = None
- self.channelOffset = None
- # initialize member variables with error checks
- self.setBackgroundColour(colour)
- self.setChannelMatrix(channelMatrix)
- self.setChannelOffset(channelOffset)
-
- def setBackgroundColour(self, colour: QColor):
- if isinstance(colour, QColor) and colour.isValid():
- self.backgroundColour = colour
- else:
- raise TypeError(str(colour) + " is not a valid QColor object.")
-
- def getBackgroundColour(self) -> QColor:
- return self.backgroundColour
-
- def getChannelMatrix(self) -> QMatrix4x4:
- return self.channelMatrix
-
- def setChannelMatrix(self, matrix):
- self.channelMatrix = QMatrix4x4(matrix)
-
- def getChannelOffset(self) -> QVector4D:
- return self.channelOffset
-
- def setChannelOffset(self, vector):
- self.channelOffset = QVector4D(vector)
-
-
-glVersionProfile = QOpenGLVersionProfile()
-glVersionProfile.setVersion(2, 1)
-
-
-class DDSWidget(QOpenGLWidget):
- def __init__(self, ddsFile, ddsOptions=DDSOptions(), debugContext=False, parent=None, f=Qt.WindowType(0)):
- super(DDSWidget, self).__init__(parent, f)
-
- self.ddsFile = ddsFile
-
- self.ddsOptions = ddsOptions
-
- self.clean = True
-
- self.logger = None
-
- self.program = None
- self.transparecyProgram = None
- self.texture = None
- self.vbo = None
- self.vao = None
-
- if debugContext:
- format = QSurfaceFormat()
- format.setOption(QSurfaceFormat.FormatOption.DebugContext)
- self.setFormat(format)
- self.logger = QOpenGLDebugLogger(self)
-
- def __del__(self):
- self.cleanup()
-
- def __dtor__(self):
- self.cleanup()
-
- def initializeGL(self):
- if self.logger:
- self.logger.initialize()
- self.logger.messageLogged.connect(
- lambda message: qDebug(self.tr("OpenGL debug message: {0}").format(message.message())))
- self.logger.startLogging()
-
- gl = QOpenGLVersionFunctionsFactory.get(glVersionProfile)
- QOpenGLContext.currentContext().aboutToBeDestroyed.connect(self.cleanup)
-
- self.clean = False
-
- fragmentShader = None
- vertexShader = vertexShader2D
- if self.ddsFile.isCubemap:
- fragmentShader = fragmentShaderCube
- vertexShader = vertexShaderCube
- if QOpenGLContext.currentContext().hasExtension(b"GL_ARB_seamless_cube_map"):
- GL_TEXTURE_CUBE_MAP_SEAMLESS = 0x884F
- gl.glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)
- elif self.ddsFile.glFormat.samplerType == "F":
- fragmentShader = fragmentShaderFloat
- elif self.ddsFile.glFormat.samplerType == "UI":
- fragmentShader = fragmentShaderUInt
- else:
- fragmentShader = fragmentShaderSInt
-
- self.program = QOpenGLShaderProgram(self)
- self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertexShader)
- self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragmentShader)
- self.program.bindAttributeLocation("position", 0)
- self.program.bindAttributeLocation("texCoordIn", 1)
- self.program.link()
-
- self.transparecyProgram = QOpenGLShaderProgram(self)
- self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, transparencyVS)
- self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, transparencyFS)
- self.transparecyProgram.bindAttributeLocation("position", 0)
- self.transparecyProgram.link()
-
- self.vao = QOpenGLVertexArrayObject(self)
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
-
- self.vbo = QOpenGLBuffer(QOpenGLBuffer.Type.VertexBuffer)
- self.vbo.create()
- self.vbo.bind()
-
- theBytes = struct.pack("%sf" % len(vertices), *vertices)
- self.vbo.allocate(theBytes, len(theBytes))
-
- gl.glEnableVertexAttribArray(0)
- gl.glEnableVertexAttribArray(1)
- gl.glVertexAttribPointer(0, 4, gl.GL_FLOAT, False, 6 * 4, 0)
- gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, False, 6 * 4, 4 * 4)
-
- self.texture = self.ddsFile.asQOpenGLTexture(gl, QOpenGLContext.currentContext())
-
- def resizeGL(self, w, h):
- aspectRatioTex = self.texture.width() / self.texture.height() if self.texture else 1.0
- aspectRatioWidget = w / h
- ratioRatio = aspectRatioTex / aspectRatioWidget
-
- self.program.bind()
- self.program.setUniformValue("aspectRatioRatio", ratioRatio)
- self.program.release()
-
- def paintGL(self):
- gl = QOpenGLVersionFunctionsFactory.get(glVersionProfile)
-
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
-
- # Draw checkerboard so transparency is obvious
- self.transparecyProgram.bind()
-
- backgroundColour = self.ddsOptions.getBackgroundColour()
- if backgroundColour and backgroundColour.isValid():
- self.transparecyProgram.setUniformValue("backgroundColour", backgroundColour)
-
- gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
-
- self.transparecyProgram.release()
-
- self.program.bind()
-
- if self.texture:
- self.texture.bind()
-
- gl.glEnable(gl.GL_BLEND)
- gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
-
- self.program.setUniformValue("channelMatrix", self.ddsOptions.getChannelMatrix())
- self.program.setUniformValue("channelOffset", self.ddsOptions.getChannelOffset())
-
- gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
-
- if self.texture:
- self.texture.release()
- self.program.release()
-
- def cleanup(self):
- if not self.clean:
- self.makeCurrent()
-
- self.program = None
- self.transparecyProgram = None
- if self.texture:
- self.texture.destroy()
- self.texture = None
- self.vbo.destroy()
- self.vbo = None
- self.vao.destroy()
- self.vao = None
-
- self.doneCurrent()
- self.clean = True
-
- def tr(self, str):
- return QCoreApplication.translate("DDSWidget", str)
-
-
-class ColourChannels(enum.Enum):
- RGBA = "Colour and Alpha"
- RGB = "Colour"
- A = "Alpha"
- R = "Red"
- G = "Green"
- B = "Blue"
-
-
-class DDSChannelManager:
- def __init__(self, channels: ColourChannels):
- self.channels = channels
-
- def setChannels(self, options: DDSOptions, channels: ColourChannels):
- self.channels = channels
-
- def drawColour(alpha: bool):
- colorMatrix = QMatrix4x4()
- colorOffset = QVector4D()
- if not alpha:
- colorMatrix[3, 3] = 0
- colorOffset.setW(1.0)
- options.setChannelMatrix(colorMatrix)
- options.setChannelOffset(colorOffset)
-
- def drawGrayscale(channel: ColourChannels):
- colorOffset = QVector4D(0, 0, 0, 1)
- channelVector = [0, 0, 0, 0]
- if channels == ColourChannels.R:
- channelVector[0] = 1
- elif channel == ColourChannels.G:
- channelVector[1] = 1
- elif channel == ColourChannels.B:
- channelVector[2] = 1
- elif channels == ColourChannels.A:
- channelVector[3] = 1
- else:
- raise ValueError("channel must be a single color channel.")
- alphaVector = [0, 0, 0, 0]
- colorMatrix = channelVector * 3 + alphaVector
- options.setChannelMatrix(colorMatrix)
- options.setChannelOffset(colorOffset)
-
- if channels == ColourChannels.RGBA:
- drawColour(True)
- elif channels == ColourChannels.RGB:
- drawColour(False)
- else:
- drawGrayscale(channels)
-
-
-class DDSPreview(mobase.IPluginPreview):
-
- def __init__(self):
- super().__init__()
- self.__organizer = None
- self.options = None
- self.channelManager = None
-
- def init(self, organizer: mobase.IOrganizer):
- self.__organizer = organizer
- savedColour = QColor(self.pluginSetting("background r"), self.pluginSetting("background g"),
- self.pluginSetting("background b"), self.pluginSetting("background a"))
- try:
- savedChannels = ColourChannels[self.pluginSetting("channels")]
- except KeyError:
- savedChannels = ColourChannels.RGBA
- self.options = DDSOptions(savedColour)
- self.channelManager = DDSChannelManager(savedChannels)
- self.channelManager.setChannels(self.options, savedChannels)
- return True
-
- def pluginSetting(self, name):
- return self.__organizer.pluginSetting(self.name(), name)
-
- def setPluginSetting(self, name, value):
- self.__organizer.setPluginSetting(self.name(), name, value)
-
- def name(self):
- return "DDS Preview Plugin"
-
- def author(self):
- return "AnyOldName3"
-
- def description(self):
- return self.tr("Lets you preview DDS files by actually uploading them to the GPU.")
-
- def version(self):
- return mobase.VersionInfo(1, 0, 1, 0)
-
- def settings(self):
- return [mobase.PluginSetting("log gl errors", self.tr(
- "If enabled, log OpenGL errors and debug messages. May decrease performance."), False),
- mobase.PluginSetting("background r", self.tr("Red channel of background colour"), 0),
- mobase.PluginSetting("background g", self.tr("Green channel of background colour"), 0),
- mobase.PluginSetting("background b", self.tr("Blue channel of background colour"), 0),
- mobase.PluginSetting("background a", self.tr("Alpha channel of background colour"), 0),
- mobase.PluginSetting("channels", self.tr("The colour channels that are displayed."),
- ColourChannels.RGBA.name)]
-
- def supportedExtensions(self) -> set[str]:
- return {"dds"}
-
- def supportsArchives(self) -> bool:
- return True
-
- def genFilePreview(self, fileName: str, maxSize: QSize) -> QWidget:
- return self.previewFromDDSFile(DDSFile.fromFile(fileName))
-
- def genDataPreview(self, fileData: bytes, fileName: str, maxSize: QSize) -> QWidget:
- return self.previewFromDDSFile(DDSFile(fileData, fileName))
-
- def previewFromDDSFile(self, ddsFile: DDSFile) -> QWidget:
- ddsFile.load()
- layout = QGridLayout()
- # Image grows before label and button
- layout.setRowStretch(0, 1)
- # Label grows before button
- layout.setColumnStretch(0, 1)
- layout.addWidget(self.__makeLabel(ddsFile), 1, 0, 1, 1)
-
- ddsWidget = DDSWidget(ddsFile, self.options, self.__organizer.pluginSetting(self.name(), "log gl errors"))
- layout.addWidget(ddsWidget, 0, 0, 1, 3)
-
- layout.addWidget(self.__makeColourButton(ddsWidget), 1, 2, 1, 1)
- layout.addWidget(self.__makeChannelsButton(ddsWidget), 1, 1, 1, 1)
-
- widget = QWidget()
- widget.setLayout(layout)
- return widget
-
- def tr(self, str):
- return QCoreApplication.translate("DDSPreview", str)
-
- def __makeLabel(self, ddsFile):
- label = QLabel(ddsFile.getDescription())
- label.setWordWrap(True)
- label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
- return label
-
- def __makeColourButton(self, ddsWidget):
- button = QPushButton(self.tr("Pick background colour"))
-
- def pickColour(unused):
- newColour = QColorDialog.getColor(self.options.getBackgroundColour(), button, "Background colour",
- QColorDialog.ColorDialogOption.ShowAlphaChannel)
- if newColour.isValid():
- self.setPluginSetting("background r", newColour.red())
- self.setPluginSetting("background g", newColour.green())
- self.setPluginSetting("background b", newColour.blue())
- self.setPluginSetting("background a", newColour.alpha())
- self.options.setBackgroundColour(newColour)
- ddsWidget.update()
-
- button.clicked.connect(pickColour)
- return button
-
- def __makeChannelsButton(self, ddsWidget):
- listwidget = QComboBox()
- channelKeys = [e.name for e in ColourChannels]
- channelNames = [e.value for e in ColourChannels]
-
- listwidget.addItems(channelNames)
- listwidget.setCurrentText(self.channelManager.channels.value)
- listwidget.setToolTip(self.tr("Select what colour channels are displayed."))
-
- listwidget.showEvent = lambda _: listwidget.setCurrentText(self.channelManager.channels.value)
-
- def onChanged(newIndex):
- self.channelManager.setChannels(self.options, ColourChannels[channelKeys[newIndex]])
- self.setPluginSetting("channels", self.channelManager.channels.name)
- ddsWidget.update()
-
- listwidget.currentIndexChanged.connect(onChanged)
- return listwidget
-
-
-def createPlugin():
- return DDSPreview()