Skip to content

Commit 5abd991

Browse files
authored
Dependencies such as PIL should now automatically download the first time that the addon is activated. I believed this was the case since before but found out now that I updated my Blender version that the feature was broken (until now).
1 parent 83c5113 commit 5abd991

1 file changed

Lines changed: 104 additions & 42 deletions

File tree

fast_pbr_viewport_render/__init__.py

Lines changed: 104 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,68 @@
11
from __future__ import annotations
2+
3+
bl_info = {
4+
"name": "Fast PBR Viewport Render",
5+
"blender": (3, 00, 0),
6+
"category": "Object",
7+
}
8+
__name__ # This is the name of the folder that the __init__.py is in. I think
9+
addonName: str = bl_info["name"]
10+
addonNameShort: str = "Fast PBR"
11+
212
from typing import Any, Mapping, ValuesView
313
from typing import TYPE_CHECKING
414

5-
from PIL import ImageChops
615

716
import bpy
817

918
import bpy
1019
import subprocess
1120
import sys
12-
21+
import ctypes
1322
from bpy.types import MaterialSlot, PropertyGroup
23+
# print("LOOK HERE ", sys.executable)
24+
import os
25+
import importlib
26+
def putTextInBox(text):
27+
lines = text.splitlines()
28+
width = max(len(s) for s in lines)
29+
res = ['┌' + '─' * width + '┐']
30+
for s in lines:
31+
res.append('│' + (s + ' ' * width)[:width] + '│')
32+
res.append('└' + '─' * width + '┘')
33+
return '\n'.join(res)
34+
import pip
35+
def installPackage(package):
36+
if hasattr(pip, 'main'):
37+
pip.main(['install', package])
38+
else:
39+
pip._internal.main(['install', package])
40+
41+
# def installPackage(package: str):
42+
# # subprocess.check_call(['"' + sys.executable + '"', "-m", "pip", "install", package])
43+
# # os.system(f'"{sys.executable}" -m pip install {package}')
44+
# ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f"""-c import pdb; pdb.set_trace()\nimport os\nos.system('"{sys.executable}" -m pip install {package}')\ninput()""", None, 1)
45+
46+
# Running installPackage is a slow operation, therefore we want to make sure that we only run it when necessary, hence this function to speed up registration/activation time of the addon SIGNIFICANTLY (like by 5-10 seconds).
47+
def attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist(packageName, moduleName):
48+
print("Attempting")
49+
try:
50+
importlib.import_module(moduleName)
51+
# from PIL import Image
52+
except Exception as error:
53+
print(putTextInBox(f"{addonName}Error: ---\n{error}\n---\nwhen attempting to import {moduleName}, we're assuming that you dont have {packageName} installed and will try to install it for you!"))
54+
installPackage(packageName)
55+
importlib.import_module(moduleName) # Doesnt actually work?
56+
ListOfModulesToAttemptToImportAndInstallItIfItIfTheCorespondingPackageDoesntExist = [['PILLOW', 'PIL'], ['numpy']]
57+
attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('PILLOW', 'PIL')
58+
import PIL
59+
attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('numpy', 'numpy')
60+
import numpy
61+
62+
63+
from PIL import ImageChops
64+
1465

15-
bl_info = {
16-
"name": "Fast PBR Viewport Render",
17-
"blender": (3, 00, 0),
18-
"category": "Object",
19-
}
2066

2167
def copyModifiers(copyAllModifiersAndThereSettingsFromObject: bpy.types.Object, copyTargetObjects: list(bpy.types.Object)):
2268
# copyAllModifiersAndThereSettingsFromObject = bpy.context.object
@@ -54,37 +100,9 @@ def copyModifiers(copyAllModifiersAndThereSettingsFromObject: bpy.types.Object,
54100

55101
# installPackage('PILLOW')
56102
# installPackage('numpy')
57-
__name__ # This is the name of the folder that the __init__.py is in. I think
58-
addonName: str = bl_info["name"]
59-
addonNameShort: str = "Fast PBR"
60103

61-
import importlib
62-
def putTextInBox(text):
63-
lines = text.splitlines()
64-
width = max(len(s) for s in lines)
65-
res = ['┌' + '─' * width + '┐']
66-
for s in lines:
67-
res.append('│' + (s + ' ' * width)[:width] + '│')
68-
res.append('└' + '─' * width + '┘')
69-
return '\n'.join(res)
70-
def installPackage(package: str):
71-
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
72104

73-
# Running installPackage is a slow operation, therefore we want to make sure that we only run it when necessary, hence this function to speed up registration/activation time of the addon SIGNIFICANTLY (like by 5-10 seconds).
74-
def attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist(packageName, moduleName):
75-
print("Attempting")
76-
try:
77-
importlib.import_module(moduleName)
78-
# from PIL import Image
79-
except Exception as error:
80-
print(putTextInBox(f"{addonName}Error: ---\n{error}\n---\nwhen attempting to import {moduleName}, we're assuming that you dont have {packageName} installed and will try to install it for you!"))
81-
installPackage(packageName)
82-
importlib.import_module(moduleName) # Doesnt actually work?
83-
ListOfModulesToAttemptToImportAndInstallItIfItIfTheCorespondingPackageDoesntExist = [['PILLOW', 'PIL'], ['numpy']]
84-
attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('PILLOW', 'PIL')
85-
import PIL
86-
attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('numpy', 'numpy')
87-
import numpy
105+
88106
# import pi
89107
# attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('PILLOW', 'PIL')
90108
# attemptToImportModuleAndInstallItIfItIfTheCorespondingPackageDoesntExist('numpy', 'numpy')
@@ -1223,7 +1241,8 @@ def restoreSettings(self):
12231241
# subtype="FILE_PATH")
12241242

12251243
class RenderPass(FastPanelBaseClass, bpy.types.Panel):
1226-
bl_options = {'DRAW_BOX', 'DEFAULT_CLOSED'}
1244+
bl_options = {'DEFAULT_CLOSED'}
1245+
# bl_options = {'DRAW_BOX', 'DEFAULT_CLOSED'}
12271246
# @registerOperatorsDecorator
12281247

12291248
class settings(bpy.types.PropertyGroup): # CANNOT BE RENAMED
@@ -1336,7 +1355,7 @@ def save_to_disk(self):
13361355
# bpy.ops.image.
13371356

13381357
@classmethod
1339-
def perform_post_process(self):
1358+
def perform_post_process(self = None):
13401359
"""Runs after save_to_disk"""
13411360
pass
13421361

@@ -1405,7 +1424,7 @@ def prepare_to_render(self):
14051424
# raise ValueError('A very specific bad thing happened.')
14061425

14071426
@classmethod
1408-
def perform_post_process(self):
1427+
def perform_post_process(self = None):
14091428
pathToStoreImageInIncludingFileNameAndFileExtension = pathToStoreImagesIn
14101429
fileToPerformPostProcessOn = pathToStoreImageInIncludingFileNameAndFileExtension + self.passName + ".png"
14111430

@@ -1497,11 +1516,18 @@ def prepare_to_render(self):
14971516
bpy.context.scene.view_settings.gamma = 1
14981517
bpy.context.scene.sequencer_colorspace_settings.name = 'Raw'
14991518

1519+
# Render properties > Sampling
1520+
bpy.context.scene.eevee.taa_samples = 1
1521+
bpy.context.scene.eevee.taa_render_samples = 1
1522+
1523+
15001524
@classmethod
15011525
def perform_post_process(cls):
15021526
# return
15031527
# Now we will invert the pixels where the normal map has rendered a face that is facing away from the camera.
15041528

1529+
1530+
15051531
# This will let users simply not care about correcting there face normals before rendering, so even if it may
15061532
# add 3-4 seconds worth of render time - its probably gonna save the user more time than if he had to ensure
15071533
# his normals are correct.
@@ -1592,9 +1618,7 @@ def perform_post_process(cls):
15921618
# image_with_inverted_green = Image.merge('RGB', (invert(red), invert(green), blue))
15931619
# image_with_inverted_green.save('test_inverted_green.tif')
15941620

1595-
# os.remove(normalWithBackfaceCulling)
1596-
1597-
1621+
os.remove(normalWithBackfaceCulling)
15981622
bpy.context.scene.display.shading.show_backface_culling = False
15991623

16001624

@@ -1646,8 +1670,15 @@ def prepare_to_render(self):
16461670
class RenderMatIDPassWithWorkbenchViewportDisplayCol(RenderPass):
16471671
passName = "MatID"
16481672

1673+
1674+
display_device = ''
1675+
view_transform = ''
1676+
sequencer_colorspace_settings_name = ''
1677+
1678+
16491679
@classmethod
16501680
def prepare_to_render(self):
1681+
self: RenderMatIDPassWithWorkbenchViewportDisplayCol
16511682

16521683
# # 3D Viewport > Viewport shading (top right) > Render Pass
16531684
# bpy.context.space_data.shading.render_pass = 'DIFFUSE_COLOR'
@@ -1664,11 +1695,29 @@ def prepare_to_render(self):
16641695
# Render properties > Options
16651696
bpy.context.scene.display.shading.show_cavity = False
16661697

1698+
1699+
# Render Properties > Color Management
1700+
self.display_device = bpy.context.scene.display_settings.display_device
1701+
bpy.context.scene.display_settings.display_device = 'sRGB'
1702+
1703+
self.view_transform = bpy.context.scene.view_settings.view_transform
1704+
bpy.context.scene.view_settings.view_transform = 'Filmic'
1705+
1706+
self.sequencer_colorspace_settings_name = bpy.context.scene.sequencer_colorspace_settings.name
1707+
bpy.context.scene.sequencer_colorspace_settings.name = 'Filmic Log'
1708+
1709+
16671710
# for material in bpy.data.materials:
16681711
# material: bpy.types.Material
16691712
# material.diffuse_color = material.node_tree.nodes
16701713
# bpy.data.materials["Material"].node_tree.nodes["Principled BSDF"].inputs[0].default_value = (0.8, 0.272124, 0.414903, 1)
16711714

1715+
@classmethod
1716+
def perform_post_process(self):
1717+
bpy.context.scene.display_settings.display_device = self.display_device
1718+
bpy.context.scene.view_settings.view_transform = self.view_transform
1719+
bpy.context.scene.sequencer_colorspace_settings.name = self.sequencer_colorspace_settings_name
1720+
16721721

16731722
# @bookmark AO pass
16741723
@renderPassDecorator
@@ -1735,6 +1784,7 @@ class RenderTransparencyPassWithEeveeEnvironmentPass(RenderPass):
17351784
# settings["High quality transparency"] = False
17361785

17371786
restoreDisplayDevice = ''
1787+
restoreViewtransform = ''
17381788
restoreSequencer = ''
17391789

17401790

@@ -1753,16 +1803,28 @@ def prepare_to_render(self):
17531803
# Render properties > Color
17541804
bpy.context.scene.display.shading.single_color = (999, 999, 999)
17551805

1806+
# World properties > Surface > color
1807+
bpy.context.scene.world.color = (9999, 9999, 9999)
1808+
1809+
17561810
self.restoreDisplayDevice = bpy.context.scene.display_settings.display_device
17571811
bpy.context.scene.display_settings.display_device = 'None'
17581812

1813+
# self.restoreViewtransform = bpy.context.scene.view_settings.view_transform
1814+
# bpy.context.scene.view_settings.view_transform = 'Raw'
1815+
1816+
17591817
self.restoreSequencer = bpy.context.scene.sequencer_colorspace_settings.name
17601818
bpy.context.scene.sequencer_colorspace_settings.name = 'Raw'
17611819

1820+
1821+
17621822

1823+
@classmethod
17631824
def perform_post_process(self):
17641825
# Inverts the alpha pass so that white means visible and black means invisible.
17651826
bpy.context.scene.display_settings.display_device = self.restoreDisplayDevice
1827+
# bpy.context.scene.view_settings.view_transform = self.restoreViewtransform
17661828
bpy.context.scene.sequencer_colorspace_settings.name = self.restoreSequencer
17671829

17681830
pathToStoreImageInIncludingFileNameAndFileExtension = pathToStoreImagesIn

0 commit comments

Comments
 (0)