The `Image.gl_load()` call was changed in Blender commit 7ad802cf3ae500bc72863b6dba0f28a488fce3d1; the two parameters we were using were removed. This commit fixes the exception and makes the texture browser usable again, but doesn't properly fix everything. The textures are drawn in the wrong colour space, which will be fixed in another commit once I know how.
193 lines
6.3 KiB
Python
193 lines
6.3 KiB
Python
import logging
|
|
import os.path
|
|
|
|
import bpy
|
|
import bgl
|
|
|
|
import pillarsdk
|
|
from . import nodes
|
|
|
|
if bpy.app.version < (2, 80):
|
|
from . import draw_27 as draw
|
|
else:
|
|
from . import draw
|
|
|
|
|
|
library_icons_path = os.path.join(os.path.dirname(__file__), "icons")
|
|
|
|
ICON_WIDTH = 128
|
|
ICON_HEIGHT = 128
|
|
|
|
|
|
class MenuItem:
|
|
"""GUI menu item for the 3D View GUI."""
|
|
|
|
icon_margin_x = 4
|
|
icon_margin_y = 4
|
|
text_margin_x = 6
|
|
|
|
text_size = 12
|
|
text_size_small = 10
|
|
|
|
DEFAULT_ICONS = {
|
|
'FOLDER': os.path.join(library_icons_path, 'folder.png'),
|
|
'SPINNER': os.path.join(library_icons_path, 'spinner.png'),
|
|
'ERROR': os.path.join(library_icons_path, 'error.png'),
|
|
}
|
|
|
|
FOLDER_NODE_TYPES = {'group_texture', 'group_hdri',
|
|
nodes.UpNode.NODE_TYPE, nodes.ProjectNode.NODE_TYPE}
|
|
SUPPORTED_NODE_TYPES = {'texture', 'hdri'}.union(FOLDER_NODE_TYPES)
|
|
|
|
def __init__(self, node, file_desc, thumb_path: str, label_text):
|
|
self.log = logging.getLogger('%s.MenuItem' % __name__)
|
|
if node['node_type'] not in self.SUPPORTED_NODE_TYPES:
|
|
self.log.info('Invalid node type in node: %s', node)
|
|
raise TypeError('Node of type %r not supported; supported are %r.' % (
|
|
node['node_type'], self.SUPPORTED_NODE_TYPES))
|
|
|
|
assert isinstance(node, pillarsdk.Node), 'wrong type for node: %r' % type(node)
|
|
assert isinstance(node['_id'], str), 'wrong type for node["_id"]: %r' % type(node['_id'])
|
|
self.node = node # pillarsdk.Node, contains 'node_type' key to indicate type
|
|
self.file_desc = file_desc # pillarsdk.File object, or None if a 'folder' node.
|
|
self.label_text = label_text
|
|
self.small_text = self._small_text_from_node()
|
|
self._thumb_path = ''
|
|
self.icon = None
|
|
self._is_folder = node['node_type'] in self.FOLDER_NODE_TYPES
|
|
self._is_spinning = False
|
|
|
|
# Determine sorting order.
|
|
# by default, sort all the way at the end and folders first.
|
|
self._order = 0 if self._is_folder else 10000
|
|
if node and node.properties and node.properties.order is not None:
|
|
self._order = node.properties.order
|
|
|
|
self.thumb_path = thumb_path
|
|
|
|
# Updated when drawing the image
|
|
self.x = 0
|
|
self.y = 0
|
|
self.width = 0
|
|
self.height = 0
|
|
|
|
def _small_text_from_node(self) -> str:
|
|
"""Return the components of the texture (i.e. which map types are available)."""
|
|
|
|
if not self.node:
|
|
return ''
|
|
|
|
try:
|
|
node_files = self.node.properties.files
|
|
except AttributeError:
|
|
# Happens for nodes that don't have .properties.files.
|
|
return ''
|
|
if not node_files:
|
|
return ''
|
|
|
|
map_types = {f.map_type for f in node_files if f.map_type}
|
|
map_types.discard('color') # all textures have colour
|
|
if not map_types:
|
|
return ''
|
|
return ', '.join(sorted(map_types))
|
|
|
|
def sort_key(self):
|
|
"""Key for sorting lists of MenuItems."""
|
|
return self._order, self.label_text
|
|
|
|
@property
|
|
def thumb_path(self) -> str:
|
|
return self._thumb_path
|
|
|
|
@thumb_path.setter
|
|
def thumb_path(self, new_thumb_path: str):
|
|
self._is_spinning = new_thumb_path == 'SPINNER'
|
|
|
|
self._thumb_path = self.DEFAULT_ICONS.get(new_thumb_path, new_thumb_path)
|
|
if self._thumb_path:
|
|
self.icon = bpy.data.images.load(filepath=self._thumb_path)
|
|
else:
|
|
self.icon = None
|
|
|
|
@property
|
|
def node_uuid(self) -> str:
|
|
return self.node['_id']
|
|
|
|
def represents(self, node) -> bool:
|
|
"""Returns True iff this MenuItem represents the given node."""
|
|
|
|
node_uuid = node['_id']
|
|
return self.node_uuid == node_uuid
|
|
|
|
def update(self, node, file_desc, thumb_path: str, label_text=None):
|
|
# We can get updated information about our Node, but a MenuItem should
|
|
# always represent one node, and it shouldn't be shared between nodes.
|
|
if self.node_uuid != node['_id']:
|
|
raise ValueError("Don't change the node ID this MenuItem reflects, "
|
|
"just create a new one.")
|
|
self.node = node
|
|
self.file_desc = file_desc # pillarsdk.File object, or None if a 'folder' node.
|
|
self.thumb_path = thumb_path
|
|
|
|
if label_text is not None:
|
|
self.label_text = label_text
|
|
|
|
if thumb_path == 'ERROR':
|
|
self.small_text = 'This open is broken'
|
|
else:
|
|
self.small_text = self._small_text_from_node()
|
|
|
|
@property
|
|
def is_folder(self) -> bool:
|
|
return self._is_folder
|
|
|
|
@property
|
|
def is_spinning(self) -> bool:
|
|
return self._is_spinning
|
|
|
|
def update_placement(self, x, y, width, height):
|
|
"""Use OpenGL to draw this one menu item."""
|
|
|
|
self.x = x
|
|
self.y = y
|
|
self.width = width
|
|
self.height = height
|
|
|
|
def draw(self, highlighted: bool):
|
|
bgl.glEnable(bgl.GL_BLEND)
|
|
if highlighted:
|
|
color = (0.555, 0.555, 0.555, 0.8)
|
|
else:
|
|
color = (0.447, 0.447, 0.447, 0.8)
|
|
|
|
draw.aabox((self.x, self.y), (self.x + self.width, self.y + self.height), color)
|
|
|
|
texture = self.icon
|
|
if texture:
|
|
err = draw.load_texture(texture)
|
|
assert not err, 'OpenGL error: %i' % err
|
|
|
|
# ------ TEXTURE ---------#
|
|
if texture:
|
|
draw.bind_texture(texture)
|
|
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
draw.aabox_with_texture(
|
|
(self.x + self.icon_margin_x, self.y),
|
|
(self.x + self.icon_margin_x + ICON_WIDTH, self.y + ICON_HEIGHT),
|
|
)
|
|
bgl.glDisable(bgl.GL_BLEND)
|
|
|
|
if texture:
|
|
texture.gl_free()
|
|
|
|
# draw some text
|
|
text_x = self.x + self.icon_margin_x + ICON_WIDTH + self.text_margin_x
|
|
text_y = self.y + ICON_HEIGHT * 0.5 - 0.25 * self.text_size
|
|
draw.text((text_x, text_y), self.label_text, fsize=self.text_size)
|
|
draw.text((text_x, self.y + 0.5 * self.text_size_small), self.small_text,
|
|
fsize=self.text_size_small, rgba=(1.0, 1.0, 1.0, 0.5))
|
|
|
|
def hits(self, mouse_x: int, mouse_y: int) -> bool:
|
|
return self.x < mouse_x < self.x + self.width and self.y < mouse_y < self.y + self.height
|