Skip to content

Commit

Permalink
Select which color channels are displayed
Browse files Browse the repository at this point in the history
  • Loading branch information
Jampi0n committed Jun 27, 2022
1 parent 5ff9585 commit 14ec98b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 55 deletions.
133 changes: 96 additions & 37 deletions src/DDSPreview.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import struct
import sys
import threading
import enum

from PyQt5.QtCore import QCoreApplication, qDebug, Qt
from PyQt5.QtGui import QColor, QOpenGLBuffer, QOpenGLContext, QOpenGLDebugLogger, QOpenGLShader, QOpenGLShaderProgram, QOpenGLTexture, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QSurfaceFormat
from PyQt5.QtWidgets import QCheckBox, QDialog, QGridLayout, QLabel, QOpenGLWidget, QPushButton, QWidget
from PyQt5.QtGui import QColor, QOpenGLBuffer, QOpenGLContext, QOpenGLDebugLogger, QOpenGLShader, QOpenGLShaderProgram, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QSurfaceFormat
from PyQt5.QtWidgets import QColorDialog, QGridLayout, QLabel, QOpenGLWidget, QPushButton, QWidget, QComboBox

from DDS.DDSFile import DDSFile

Expand Down Expand Up @@ -146,17 +147,51 @@
]


class ColourChannels(enum.Enum):
RGBA = "Colour and Alpha"
RGB = "Colour"
A = "Alpha"
R = "Red"
G = "Green"
B = "Blue"


class DDSOptions:
def __init__(self, colour: QColor = QColor(0, 0, 0, 0), channels: ColourChannels = ColourChannels.RGBA):
self.backgroundColour = None
self.channels = None
self.setBackgroundColour(colour)
self.setChannels(channels)

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 setChannels(self, channels: ColourChannels):
if isinstance(channels, ColourChannels):
self.channels = channels
else:
raise TypeError(str(channels) + " is not a valid ColourChannels object.")

def getChannels(self) -> ColourChannels:
return self.channels


glVersionProfile = QOpenGLVersionProfile()
glVersionProfile.setVersion(2, 1)

class DDSWidget(QOpenGLWidget):
def __init__(self, ddsPreview, ddsFile, debugContext = False, parent = None, f = Qt.WindowFlags()):
def __init__(self, ddsFile, ddsOptions = DDSOptions(), debugContext = False, parent = None, f = Qt.WindowFlags()):
super(DDSWidget, self).__init__(parent, f)

self.ddsPreview = ddsPreview

self.ddsFile = ddsFile

self.ddsOptions = ddsOptions

self.clean = True

Expand Down Expand Up @@ -253,7 +288,7 @@ def paintGL(self):
# Draw checkerboard so transparency is obvious
self.transparecyProgram.bind()

backgroundColour = self.ddsPreview.getBackgroundColour()
backgroundColour = self.ddsOptions.getBackgroundColour()
if backgroundColour and backgroundColour.isValid():
self.transparecyProgram.setUniformValue("backgroundColour", backgroundColour)

Expand All @@ -266,11 +301,35 @@ def paintGL(self):
if self.texture:
self.texture.bind()

if self.ddsPreview.getTransparency():
gl.glEnable(gl.GL_BLEND)
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
gl.glEnable(gl.GL_BLEND)
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)

def drawColour(alpha):
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_R, gl.GL_RED)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_G, gl.GL_GREEN)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_B, gl.GL_BLUE)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_A, alpha)

def drawGrayscale(channel):
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_R, channel)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_G, channel)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_B, channel)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_SWIZZLE_A, gl.GL_ONE)

This comment has been minimized.

Copy link
@AnyOldName3

AnyOldName3 Jun 27, 2022

Member

Annoyingly, swizzles were only made core in OpenGL 3.3, and this plugin uses OpenGL 2.1 for maximum compatibility (older versions than that add quite a few restrictions without increasing the range of compatible hardware by much). I've seen people playing Morrowind on pre-OpenGL-3.0 hardware, so it's probably best not to bump the minimum version and instead pass a uniform controlling this to the shader.


if self.ddsOptions.getChannels() == ColourChannels.RGBA:
drawColour(gl.GL_ALPHA)
elif self.ddsOptions.getChannels() == ColourChannels.RGB:
drawColour(gl.GL_ONE)
elif self.ddsOptions.getChannels() == ColourChannels.R:
drawGrayscale(gl.GL_RED)
elif self.ddsOptions.getChannels() == ColourChannels.G:
drawGrayscale(gl.GL_GREEN)
elif self.ddsOptions.getChannels() == ColourChannels.B:
drawGrayscale(gl.GL_BLUE)
elif self.ddsOptions.getChannels() == ColourChannels.A:
drawGrayscale(gl.GL_ALPHA)
else:
gl.glDisable(gl.GL_BLEND)
drawGrayscale(gl.GL_ZERO)

gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

Expand Down Expand Up @@ -304,9 +363,16 @@ class DDSPreview(mobase.IPluginPreview):
def __init__(self):
super().__init__()
self.__organizer = None
self.options = None

def init(self, organizer):
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, savedChannels)
return True

def pluginSetting(self, name):
Expand All @@ -333,7 +399,7 @@ def settings(self):
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("transparency", self.__tr("If enabled, transparency will be displayed."), True)]
mobase.PluginSetting("channels", self.__tr("The colour channels that are displayed."), ColourChannels.RGBA.name)]

def supportedExtensions(self):
return ["dds"]
Expand All @@ -348,11 +414,11 @@ def genFilePreview(self, fileName, maxSize):
layout.setColumnStretch(0, 1)
layout.addWidget(self.__makeLabel(ddsFile), 1, 0, 1, 1)

ddsWidget = DDSWidget(self, ddsFile, self.__organizer.pluginSetting(self.name(), "log gl errors"))
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.__makeToggleTransparencyButton(ddsWidget), 1, 1, 1, 1)
layout.addWidget(self.__makeChannelsButton(ddsWidget), 1, 1, 1, 1)

widget = QWidget()
widget.setLayout(layout)
Expand All @@ -371,42 +437,35 @@ def __makeColourButton(self, ddsWidget):
button = QPushButton(self.__tr("Pick background colour"))

def pickColour(unused):
newColour = QColorDialog.getColor(self.getBackgroundColour(), button, "Background colour", QColorDialog.ShowAlphaChannel)
newColour = QColorDialog.getColor(self.options.getBackgroundColour(), button, "Background colour", QColorDialog.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 __makeToggleTransparencyButton(self, ddsWidget):
def getButtonText():
if self.getTransparency():
return self.__tr("Disable Transparency")
else:
return self.__tr("Enable Transparency")

button = QPushButton(getButtonText())
# Since every conflicting mod gets its own button, the text needs to be updated when the page is changed.
button.showEvent = lambda _: button.setText(getButtonText())

def toggleTransparency(unused):
transparency = not self.getTransparency()
self.setPluginSetting("transparency", transparency)
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.options.channels.value)
listwidget.setToolTip(self.__tr("Change which channels are displayed."))

listwidget.showEvent = lambda _: listwidget.setCurrentText(self.options.channels.value)
def onChanged(newIndex):
self.options.channels = ColourChannels[channelKeys[newIndex]]
self.setPluginSetting("channels", self.options.channels.name)
ddsWidget.update()
button.setText(getButtonText())

button.clicked.connect(toggleTransparency)
return button

def getBackgroundColour(self):
return QColor(self.pluginSetting("background r"), self.pluginSetting("background g"), self.pluginSetting("background b"), self.pluginSetting("background a"))

def getTransparency(self):
return self.pluginSetting("transparency")
listwidget.currentIndexChanged.connect(onChanged)
return listwidget

def createPlugin():
return DDSPreview()
32 changes: 14 additions & 18 deletions src/DDSPreview_en.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0">
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>DDSFile</name>
<message>
Expand Down Expand Up @@ -71,60 +72,55 @@
<context>
<name>DDSPreview</name>
<message>
<location filename="DDSPreview.py" line="325"/>
<location filename="DDSPreview.py" line="391"/>
<source>Lets you preview DDS files by actually uploading them to the GPU.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="331"/>
<location filename="DDSPreview.py" line="397"/>
<source>If enabled, log OpenGL errors and debug messages. May decrease performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="332"/>
<location filename="DDSPreview.py" line="398"/>
<source>Red channel of background colour</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="333"/>
<location filename="DDSPreview.py" line="399"/>
<source>Green channel of background colour</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="334"/>
<location filename="DDSPreview.py" line="400"/>
<source>Blue channel of background colour</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="335"/>
<location filename="DDSPreview.py" line="401"/>
<source>Alpha channel of background colour</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="336"/>
<source>If enabled, transparency will be displayed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="371"/>
<location filename="DDSPreview.py" line="437"/>
<source>Pick background colour</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="388"/>
<source>Disable Transparency</source>
<location filename="DDSPreview.py" line="402"/>
<source>The colour channels that are displayed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DDSPreview.py" line="390"/>
<source>Enable Transparency</source>
<location filename="DDSPreview.py" line="459"/>
<source>Change which channels are displayed.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DDSWidget</name>
<message>
<location filename="DDSPreview.py" line="186"/>
<location filename="DDSPreview.py" line="221"/>
<source>OpenGL debug message: {0}</source>
<translation type="unfinished"></translation>
</message>
Expand Down

0 comments on commit 14ec98b

Please sign in to comment.