Add Chromium-only Blender WebEngine parity work
This commit is contained in:
185
blender-5.2.0/intern/cycles/blender/addon/__init__.py
Normal file
185
blender-5.2.0/intern/cycles/blender/addon/__init__.py
Normal file
@@ -0,0 +1,185 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
bl_info = {
|
||||
"name": "Cycles Render Engine",
|
||||
"author": "",
|
||||
"blender": (2, 80, 0),
|
||||
"description": "Cycles renderer integration",
|
||||
"warning": "",
|
||||
"doc_url": "https://docs.blender.org/manual/en/latest/render/cycles/",
|
||||
"tracker_url": "",
|
||||
"support": 'OFFICIAL',
|
||||
"category": "Render"}
|
||||
|
||||
# Support 'reload' case.
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
if "engine" in locals():
|
||||
importlib.reload(engine)
|
||||
if "version_update" in locals():
|
||||
importlib.reload(version_update)
|
||||
if "ui" in locals():
|
||||
importlib.reload(ui)
|
||||
if "operators" in locals():
|
||||
importlib.reload(operators)
|
||||
if "properties" in locals():
|
||||
importlib.reload(properties)
|
||||
if "presets" in locals():
|
||||
importlib.reload(presets)
|
||||
if "maketx" in locals():
|
||||
importlib.reload(maketx)
|
||||
|
||||
import bpy
|
||||
|
||||
from . import (
|
||||
engine,
|
||||
version_update,
|
||||
)
|
||||
|
||||
|
||||
class CyclesRender(bpy.types.RenderEngine):
|
||||
bl_idname = 'CYCLES'
|
||||
bl_label = "Cycles"
|
||||
bl_use_eevee_viewport = True
|
||||
bl_use_preview = True
|
||||
bl_use_exclude_layers = True
|
||||
bl_use_spherical_stereo = True
|
||||
bl_use_custom_freestyle = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.session = None
|
||||
|
||||
def __del__(self):
|
||||
engine.free(self)
|
||||
|
||||
# final render
|
||||
def update(self, data, depsgraph):
|
||||
if not self.session:
|
||||
if self.is_preview:
|
||||
cscene = bpy.context.scene.cycles
|
||||
use_osl = cscene.shading_system
|
||||
|
||||
engine.create(self, data, preview_osl=use_osl)
|
||||
else:
|
||||
engine.create(self, data)
|
||||
|
||||
engine.reset(self, data, depsgraph)
|
||||
|
||||
def render(self, depsgraph):
|
||||
engine.render(self, depsgraph)
|
||||
|
||||
def render_frame_finish(self):
|
||||
engine.render_frame_finish(self)
|
||||
|
||||
def draw(self, context, depsgraph):
|
||||
engine.draw(self, depsgraph, context.space_data)
|
||||
|
||||
def bake(self, depsgraph, obj, pass_type, pass_filter, width, height):
|
||||
engine.bake(self, depsgraph, obj, pass_type, pass_filter, width, height)
|
||||
|
||||
# viewport render
|
||||
def view_update(self, context, depsgraph):
|
||||
if not self.session:
|
||||
# When starting a new render session in viewport (by switching
|
||||
# viewport to Rendered shading) unpause the render. The way to think
|
||||
# of it is: artist requests render, so we start to render.
|
||||
# Do it for both original and evaluated scene so that Cycles
|
||||
# immediately reacts to un-paused render.
|
||||
cscene = context.scene.cycles
|
||||
cscene_eval = depsgraph.scene_eval.cycles
|
||||
if cscene.preview_pause or cscene_eval.preview_pause:
|
||||
cscene.preview_pause = False
|
||||
cscene_eval.preview_pause = False
|
||||
|
||||
engine.create(self, context.blend_data,
|
||||
context.region, context.space_data, context.region_data)
|
||||
|
||||
engine.reset(self, context.blend_data, depsgraph)
|
||||
engine.sync(self, depsgraph, context.blend_data)
|
||||
|
||||
def view_draw(self, context, depsgraph):
|
||||
engine.view_draw(self, depsgraph, context.region, context.space_data, context.region_data)
|
||||
|
||||
def update_script_node(self, node):
|
||||
if engine.with_osl():
|
||||
from . import osl
|
||||
osl.update_script_node(node, self.report)
|
||||
else:
|
||||
self.report({'ERROR'}, "OSL support disabled in this build")
|
||||
|
||||
def update_custom_camera(self, cam):
|
||||
if engine.with_osl():
|
||||
from . import osl
|
||||
osl.update_custom_camera_shader(cam, self.report)
|
||||
else:
|
||||
self.report({'ERROR'}, "OSL support disabled in this build")
|
||||
|
||||
def update_render_passes(self, scene, srl):
|
||||
engine.register_passes(self, scene, srl)
|
||||
|
||||
|
||||
def engine_exit():
|
||||
engine.exit()
|
||||
|
||||
|
||||
classes = (
|
||||
CyclesRender,
|
||||
)
|
||||
|
||||
|
||||
cli_commands = []
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
from . import ui
|
||||
from . import operators
|
||||
from . import properties
|
||||
from . import presets
|
||||
from .maketx import maketx_command
|
||||
import atexit
|
||||
|
||||
# Make sure we only registered the callback once.
|
||||
atexit.unregister(engine_exit)
|
||||
atexit.register(engine_exit)
|
||||
|
||||
engine.init()
|
||||
|
||||
properties.register()
|
||||
ui.register()
|
||||
operators.register()
|
||||
presets.register()
|
||||
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
bpy.app.handlers.version_update.append(version_update.do_versions)
|
||||
|
||||
cli_commands.append(bpy.utils.register_cli_command("maketx", maketx_command))
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
from . import ui
|
||||
from . import operators
|
||||
from . import properties
|
||||
from . import presets
|
||||
|
||||
bpy.app.handlers.version_update.remove(version_update.do_versions)
|
||||
|
||||
for cmd in cli_commands:
|
||||
bpy.utils.unregister_cli_command(cmd)
|
||||
cli_commands.clear()
|
||||
|
||||
ui.unregister()
|
||||
operators.unregister()
|
||||
properties.unregister()
|
||||
presets.unregister()
|
||||
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
78
blender-5.2.0/intern/cycles/blender/addon/camera.py
Normal file
78
blender-5.2.0/intern/cycles/blender/addon/camera.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Fit to match default projective camera with focal_length 50 and sensor_width 36.
|
||||
default_fisheye_polynomial = [
|
||||
-1.1735143712967577e-05,
|
||||
-0.019988736953434998,
|
||||
-3.3525322965709175e-06,
|
||||
3.099275275886036e-06,
|
||||
-2.6064646454854524e-08,
|
||||
]
|
||||
|
||||
|
||||
# Utilities to generate lens polynomials to match built-in camera types, only here
|
||||
# for reference at the moment, not used by the code.
|
||||
def create_grid(sensor_height, sensor_width):
|
||||
import numpy as np
|
||||
if sensor_height is None:
|
||||
sensor_height = sensor_width / (16 / 9) # Default aspect ration 16:9
|
||||
uu, vv = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
|
||||
uu = (uu - 0.5) * sensor_width
|
||||
vv = (vv - 0.5) * sensor_height
|
||||
rr = np.sqrt(uu ** 2 + vv ** 2)
|
||||
return rr
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_projective(focal_length=50, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_projective_fov(fov, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
f = sensor_width / 2 / np.tan(fov / 2)
|
||||
return fisheye_lens_polynomial_from_projective(f, sensor_width, sensor_height)
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_equisolid(lens=10.5, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
x = rr.reshape(-1)
|
||||
x = np.stack([x**i for i in [1, 2, 3, 4]])
|
||||
y = (-2 * np.arcsin(rr / (2 * lens))).reshape(-1)
|
||||
polynomial = np.linalg.lstsq(x.T, y.T, rcond=None)[0]
|
||||
return [0] + list(polynomial)
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_equidistant(fov=180, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
return [0, -np.radians(fov) / sensor_width, 0, 0, 0]
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_distorted_projective_polynomial(
|
||||
k1, k2, k3, focal_length=50, sensor_width=36, sensor_height=None,
|
||||
):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
r2 = (rr / focal_length) ** 2
|
||||
r4 = r2 * r2
|
||||
r6 = r4 * r2
|
||||
r_coeff = 1 + k1 * r2 + k2 * r4 + k3 * r6
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length * r_coeff)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_distorted_projective_divisions(
|
||||
k1, k2, focal_length=50, sensor_width=36, sensor_height=None,
|
||||
):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
r2 = (rr / focal_length) ** 2
|
||||
r4 = r2 * r2
|
||||
r_coeff = 1 + k1 * r2 + k2 * r4
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length / r_coeff)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
277
blender-5.2.0/intern/cycles/blender/addon/engine.py
Normal file
277
blender-5.2.0/intern/cycles/blender/addon/engine.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def _configure_argument_parser():
|
||||
import argparse
|
||||
# No help because it conflicts with general Python scripts argument parsing
|
||||
parser = argparse.ArgumentParser(description="Cycles Addon argument parser",
|
||||
add_help=False)
|
||||
parser.add_argument("--cycles-print-stats",
|
||||
help="Print rendering statistics to stderr",
|
||||
action='store_true')
|
||||
parser.add_argument("--cycles-device",
|
||||
help="Set the device to use for Cycles, overriding user preferences and the scene setting."
|
||||
"Valid options are 'CPU', 'CUDA', 'OPTIX', 'HIP', 'ONEAPI', or 'METAL'."
|
||||
"Additionally, you can append '+CPU' to any GPU type for hybrid rendering.",
|
||||
default=None)
|
||||
return parser
|
||||
|
||||
|
||||
def _parse_command_line():
|
||||
import sys
|
||||
|
||||
argv = sys.argv
|
||||
if "--" not in argv:
|
||||
return
|
||||
|
||||
parser = _configure_argument_parser()
|
||||
args, _ = parser.parse_known_args(argv[argv.index("--") + 1:])
|
||||
|
||||
if args.cycles_print_stats:
|
||||
import _cycles
|
||||
_cycles.enable_print_stats()
|
||||
|
||||
if args.cycles_device:
|
||||
import _cycles
|
||||
if not _cycles.set_device_override(args.cycles_device):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def init():
|
||||
import bpy
|
||||
import _cycles
|
||||
import os.path
|
||||
|
||||
path = os.path.dirname(__file__)
|
||||
user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', path='')))
|
||||
|
||||
_cycles.init(path, user_path, bpy.app.background)
|
||||
_parse_command_line()
|
||||
|
||||
|
||||
def exit():
|
||||
import _cycles
|
||||
_cycles.exit()
|
||||
|
||||
|
||||
def create(engine, data, region=None, v3d=None, rv3d=None, preview_osl=False):
|
||||
import _cycles
|
||||
import bpy
|
||||
|
||||
data = data.as_pointer()
|
||||
prefs = bpy.context.preferences.as_pointer()
|
||||
screen = 0
|
||||
if region:
|
||||
screen = region.id_data.as_pointer()
|
||||
region = region.as_pointer()
|
||||
if v3d:
|
||||
screen = screen or v3d.id_data.as_pointer()
|
||||
v3d = v3d.as_pointer()
|
||||
if rv3d:
|
||||
screen = screen or rv3d.id_data.as_pointer()
|
||||
rv3d = rv3d.as_pointer()
|
||||
|
||||
engine.session = _cycles.create(engine.as_pointer(), prefs, data, screen, region, v3d, rv3d, preview_osl)
|
||||
|
||||
|
||||
def free(engine):
|
||||
if hasattr(engine, "session"):
|
||||
if engine.session:
|
||||
import _cycles
|
||||
_cycles.free(engine.session)
|
||||
del engine.session
|
||||
|
||||
|
||||
def render(engine, depsgraph):
|
||||
import _cycles
|
||||
if hasattr(engine, "session"):
|
||||
_cycles.render(engine.session, depsgraph.as_pointer())
|
||||
|
||||
|
||||
def render_frame_finish(engine):
|
||||
if not engine.session:
|
||||
return
|
||||
|
||||
import _cycles
|
||||
_cycles.render_frame_finish(engine.session)
|
||||
|
||||
|
||||
def draw(engine, depsgraph, space_image):
|
||||
if not engine.session:
|
||||
return
|
||||
|
||||
depsgraph_ptr = depsgraph.as_pointer()
|
||||
space_image_ptr = space_image.as_pointer()
|
||||
screen_ptr = space_image.id_data.as_pointer()
|
||||
|
||||
import _cycles
|
||||
_cycles.draw(engine.session, depsgraph_ptr, screen_ptr, space_image_ptr)
|
||||
|
||||
|
||||
def bake(engine, depsgraph, obj, pass_type, pass_filter, width, height):
|
||||
import _cycles
|
||||
session = getattr(engine, "session", None)
|
||||
if session is not None:
|
||||
_cycles.bake(engine.session, depsgraph.as_pointer(), obj.as_pointer(), pass_type, pass_filter, width, height)
|
||||
|
||||
|
||||
def reset(engine, data, depsgraph):
|
||||
import _cycles
|
||||
import bpy
|
||||
|
||||
prefs = bpy.context.preferences
|
||||
if prefs.experimental.use_cycles_debug and prefs.view.show_developer_ui:
|
||||
_cycles.debug_flags_update(depsgraph.scene.as_pointer())
|
||||
else:
|
||||
_cycles.debug_flags_reset()
|
||||
|
||||
data = data.as_pointer()
|
||||
depsgraph = depsgraph.as_pointer()
|
||||
_cycles.reset(engine.session, data, depsgraph)
|
||||
|
||||
|
||||
def sync(engine, depsgraph, data):
|
||||
import _cycles
|
||||
_cycles.sync(engine.session, depsgraph.as_pointer())
|
||||
|
||||
|
||||
def view_draw(engine, depsgraph, region, v3d, rv3d):
|
||||
import _cycles
|
||||
depsgraph = depsgraph.as_pointer()
|
||||
v3d = v3d.as_pointer()
|
||||
rv3d = rv3d.as_pointer()
|
||||
|
||||
# draw render image
|
||||
_cycles.view_draw(engine.session, depsgraph, v3d, rv3d)
|
||||
|
||||
|
||||
def available_devices():
|
||||
import _cycles
|
||||
return _cycles.available_devices()
|
||||
|
||||
|
||||
def with_osl():
|
||||
import _cycles
|
||||
return _cycles.with_osl
|
||||
|
||||
|
||||
def osl_version():
|
||||
import _cycles
|
||||
return _cycles.osl_version
|
||||
|
||||
|
||||
def with_path_guiding():
|
||||
import _cycles
|
||||
return _cycles.with_path_guiding
|
||||
|
||||
|
||||
def system_info():
|
||||
import _cycles
|
||||
return _cycles.system_info()
|
||||
|
||||
|
||||
def list_render_passes(scene, srl):
|
||||
import _cycles
|
||||
from bpy.app.translations import pgettext_n as n_
|
||||
|
||||
crl = srl.cycles
|
||||
|
||||
# Combined pass.
|
||||
yield ("Combined", "RGBA", 'COLOR')
|
||||
|
||||
# Keep alignment for readability.
|
||||
# autopep8: off
|
||||
|
||||
# Data passes.
|
||||
if srl.use_pass_z: yield (n_("Depth"), "Z", 'VALUE')
|
||||
if srl.use_pass_mist: yield (n_("Mist"), "Z", 'VALUE')
|
||||
if srl.use_pass_position: yield (n_("Position"), "XYZ", 'VECTOR')
|
||||
if srl.use_pass_normal: yield (n_("Normal"), "XYZ", 'VECTOR')
|
||||
if srl.use_pass_vector: yield (n_("Vector"), "XYZW", 'VECTOR')
|
||||
if srl.use_pass_uv: yield (n_("UV"), "UVA", 'VECTOR')
|
||||
if srl.use_pass_object_index: yield (n_("Object Index"), "X", 'VALUE')
|
||||
if srl.use_pass_material_index: yield (n_("Material Index"), "X", 'VALUE')
|
||||
|
||||
# Light passes.
|
||||
if srl.use_pass_diffuse_direct: yield (n_("Diffuse Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_diffuse_indirect: yield (n_("Diffuse Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_diffuse_color: yield (n_("Diffuse Color"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_direct: yield (n_("Glossy Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_indirect: yield (n_("Glossy Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_color: yield (n_("Glossy Color"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_direct: yield (n_("Transmission Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_indirect: yield (n_("Transmission Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_color: yield (n_("Transmission Color"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_direct: yield (n_("Volume Direct"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_indirect: yield (n_("Volume Indirect"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_scatter: yield (n_("Volume Scatter"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_transmit: yield (n_("Volume Transmit"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_majorant: yield (n_("Volume Majorant"), "Z", 'VALUE')
|
||||
if srl.use_pass_emit: yield (n_("Emission"), "RGB", 'COLOR')
|
||||
if srl.use_pass_environment: yield (n_("Environment"), "RGB", 'COLOR')
|
||||
if srl.use_pass_ambient_occlusion: yield (n_("Ambient Occlusion"), "RGB", 'COLOR')
|
||||
if crl.use_pass_shadow_catcher: yield (n_("Shadow Catcher"), "RGB", 'COLOR')
|
||||
# autopep8: on
|
||||
|
||||
# Debug passes.
|
||||
if crl.pass_debug_sample_count:
|
||||
yield (n_("Debug Sample Count"), "X", 'VALUE')
|
||||
if crl.pass_render_time:
|
||||
# Only yield the pass if rendering on CPU
|
||||
if scene.cycles.device == 'CPU':
|
||||
yield (n_("Render Time"), "X", "VALUE")
|
||||
|
||||
# Cryptomatte passes.
|
||||
# NOTE: Name channels are lowercase RGBA so that compression rules check in OpenEXR DWA code
|
||||
# uses lossless compression. Reportedly this naming is the only one which works good from the
|
||||
# interoperability point of view. Using XYZW naming is not portable.
|
||||
crypto_depth = (min(16, srl.pass_cryptomatte_depth) + 1) // 2
|
||||
if srl.use_pass_cryptomatte_object:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoObject" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
if srl.use_pass_cryptomatte_material:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoMaterial" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
if srl.use_pass_cryptomatte_asset:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoAsset" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
|
||||
# Denoising passes.
|
||||
if scene.cycles.use_denoising and crl.use_denoising:
|
||||
yield (n_("Noisy Image"), "RGBA", 'COLOR')
|
||||
if crl.use_pass_shadow_catcher:
|
||||
yield (n_("Noisy Shadow Catcher"), "RGB", 'COLOR')
|
||||
if crl.denoising_store_passes:
|
||||
yield (n_("Denoising Albedo"), "RGB", 'COLOR')
|
||||
yield (n_("Denoising Specular Albedo"), "RGB", 'COLOR')
|
||||
yield (n_("Denoising Normal"), "XYZ", 'VECTOR')
|
||||
yield (n_("Denoising Roughness"), "X", 'VALUE')
|
||||
yield (n_("Denoising Depth"), "Z", 'VALUE')
|
||||
|
||||
# Custom AOV passes.
|
||||
for aov in srl.aovs:
|
||||
if not aov.is_valid:
|
||||
continue
|
||||
if aov.type == 'VALUE':
|
||||
yield (aov.name, "X", 'VALUE')
|
||||
else:
|
||||
yield (aov.name, "RGBA", 'COLOR')
|
||||
|
||||
# Light groups.
|
||||
for lightgroup in srl.lightgroups:
|
||||
yield ("Combined_%s" % lightgroup.name, "RGB", 'COLOR')
|
||||
|
||||
# Path guiding debug passes.
|
||||
if _cycles.with_debug and scene.cycles.use_guiding:
|
||||
yield (n_("Guiding Color"), "RGB", 'COLOR')
|
||||
yield (n_("Guiding Probability"), "X", 'VALUE')
|
||||
yield (n_("Guiding Average Roughness"), "X", 'VALUE')
|
||||
|
||||
|
||||
def register_passes(engine, scene, view_layer):
|
||||
for name, channelids, channeltype in list_render_passes(scene, view_layer):
|
||||
engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype)
|
||||
106
blender-5.2.0/intern/cycles/blender/addon/maketx.py
Normal file
106
blender-5.2.0/intern/cycles/blender/addon/maketx.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def argparse_create():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=pathlib.Path(sys.argv[0]).name + " --command maketx",
|
||||
description=(
|
||||
"Generate Cycles texture cache (.tx) files.\n"
|
||||
"\n"
|
||||
"When a file path is given, generate a tx file for that image.\n"
|
||||
"When no file path is given, a blend file must be loaded and tx\n"
|
||||
"files are generated for all images used by shader node trees."
|
||||
),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"filepath",
|
||||
type=str,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Source image file path. If omitted, generate for all images in the blend file.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--colorspace",
|
||||
dest="colorspace",
|
||||
type=str,
|
||||
default="auto",
|
||||
help="Color space name. Auto-detected if omitted. Only used with a file path.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--alpha-type",
|
||||
dest="alpha_type",
|
||||
type=str,
|
||||
default="auto",
|
||||
choices=["straight", "premultiplied", "channel_packed", "none", "auto"],
|
||||
help="Alpha type. Auto-detected if omitted. Only used with a file path.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--cache-dir",
|
||||
dest="cache_dir",
|
||||
type=str,
|
||||
default="",
|
||||
help="Output directory for tx files, relative to source image or absolute.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def maketx_command(argv):
|
||||
parser = argparse_create()
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.filepath is not None:
|
||||
return maketx_file(args)
|
||||
else:
|
||||
return maketx_blend(args, parser)
|
||||
|
||||
|
||||
def maketx_file(args):
|
||||
import _cycles
|
||||
|
||||
filepath = str(pathlib.Path(args.filepath).absolute())
|
||||
|
||||
try:
|
||||
out_filepath = _cycles.maketx(
|
||||
filepath,
|
||||
colorspace=args.colorspace,
|
||||
alpha_type=args.alpha_type,
|
||||
cache_dir=args.cache_dir,
|
||||
)
|
||||
except RuntimeError as ex:
|
||||
sys.stderr.write("Error: {:s}\n".format(str(ex)))
|
||||
return 1
|
||||
|
||||
print(out_filepath)
|
||||
return 0
|
||||
|
||||
|
||||
def maketx_blend(args, parser):
|
||||
import bpy
|
||||
|
||||
if not bpy.data.filepath:
|
||||
parser.error("No image file path given and no blend file loaded.")
|
||||
|
||||
if args.colorspace != "auto":
|
||||
parser.error("--colorspace is only used with an image file path argument.")
|
||||
|
||||
if args.alpha_type != "auto":
|
||||
parser.error("--alpha-type is only used with an image file path argument.")
|
||||
|
||||
if args.cache_dir:
|
||||
bpy.context.preferences.filepaths.texture_cache_directory = args.cache_dir
|
||||
|
||||
bpy.ops.render.generate_texture_cache()
|
||||
return 0
|
||||
167
blender-5.2.0/intern/cycles/blender/addon/operators.py
Normal file
167
blender-5.2.0/intern/cycles/blender/addon/operators.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import StringProperty
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class CYCLES_OT_use_shading_nodes(Operator):
|
||||
"""Enable nodes on a light"""
|
||||
bl_idname = "cycles.use_shading_nodes"
|
||||
bl_label = "Use Nodes"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return getattr(context, "light", False)
|
||||
|
||||
def execute(self, context):
|
||||
if context.light:
|
||||
context.light.use_nodes = True
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CYCLES_OT_denoise_animation(Operator):
|
||||
"Denoise rendered animation sequence using current scene and view " \
|
||||
"layer settings. Requires denoising data passes and output to " \
|
||||
"OpenEXR multilayer files"
|
||||
bl_idname = "cycles.denoise_animation"
|
||||
bl_label = "Denoise Animation"
|
||||
|
||||
input_filepath: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to denoise. If not specified, uses the render file path and frame range from the scene',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
output_filepath: StringProperty(
|
||||
name='Output Filepath',
|
||||
description='If not specified, renders will be denoised in-place',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
def execute(self, context):
|
||||
import os
|
||||
|
||||
preferences = context.preferences
|
||||
scene = context.scene
|
||||
view_layer = context.view_layer
|
||||
|
||||
in_filepath = self.input_filepath
|
||||
out_filepath = self.output_filepath
|
||||
|
||||
in_filepaths = []
|
||||
out_filepaths = []
|
||||
|
||||
if in_filepath != '':
|
||||
# Denoise a single file
|
||||
if out_filepath == '':
|
||||
out_filepath = in_filepath
|
||||
|
||||
in_filepaths.append(in_filepath)
|
||||
out_filepaths.append(out_filepath)
|
||||
else:
|
||||
# Denoise animation sequence with expanded frames matching
|
||||
# Blender render output file naming.
|
||||
in_filepath = scene.render.filepath
|
||||
if out_filepath == '':
|
||||
out_filepath = in_filepath
|
||||
|
||||
# Backup since we will overwrite the scene path temporarily
|
||||
original_filepath = scene.render.filepath
|
||||
|
||||
for frame in range(scene.frame_start, scene.frame_end + 1):
|
||||
scene.render.filepath = in_filepath
|
||||
filepath = scene.render.frame_path(frame=frame)
|
||||
in_filepaths.append(filepath)
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
scene.render.filepath = original_filepath
|
||||
err_msg = tip_("Frame '%s' not found, animation must be complete") % filepath
|
||||
self.report({'ERROR'}, err_msg)
|
||||
return {'CANCELLED'}
|
||||
|
||||
scene.render.filepath = out_filepath
|
||||
filepath = scene.render.frame_path(frame=frame)
|
||||
out_filepaths.append(filepath)
|
||||
|
||||
scene.render.filepath = original_filepath
|
||||
|
||||
# Run denoiser
|
||||
# TODO: support cancel and progress reports.
|
||||
import _cycles
|
||||
try:
|
||||
_cycles.denoise(preferences.as_pointer(),
|
||||
scene.as_pointer(),
|
||||
view_layer.as_pointer(),
|
||||
input=in_filepaths,
|
||||
output=out_filepaths)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, str(e))
|
||||
return {'FINISHED'}
|
||||
|
||||
self.report({'INFO'}, "Denoising completed")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CYCLES_OT_merge_images(Operator):
|
||||
"Combine OpenEXR multi-layer images rendered with different sample " \
|
||||
"ranges into one image with reduced noise"
|
||||
bl_idname = "cycles.merge_images"
|
||||
bl_label = "Merge Images"
|
||||
|
||||
input_filepath1: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to merge',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
input_filepath2: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to merge',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
output_filepath: StringProperty(
|
||||
name='Output Filepath',
|
||||
description='File path for merged image',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
def execute(self, context):
|
||||
in_filepaths = [self.input_filepath1, self.input_filepath2]
|
||||
out_filepath = self.output_filepath
|
||||
|
||||
import _cycles
|
||||
try:
|
||||
_cycles.merge(input=in_filepaths, output=out_filepath)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, str(e))
|
||||
return {'FINISHED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
CYCLES_OT_use_shading_nodes,
|
||||
CYCLES_OT_denoise_animation,
|
||||
CYCLES_OT_merge_images
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
375
blender-5.2.0/intern/cycles/blender/addon/osl.py
Normal file
375
blender-5.2.0/intern/cycles/blender/addon/osl.py
Normal file
@@ -0,0 +1,375 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
import _cycles
|
||||
|
||||
from bpy.app.translations import pgettext_rpt as rpt_
|
||||
|
||||
|
||||
def osl_compile(input_path, report):
|
||||
"""compile .osl file with given filepath to temporary .oso file"""
|
||||
import tempfile
|
||||
output_file = tempfile.NamedTemporaryFile(mode='w', suffix=".oso", delete=False)
|
||||
output_path = output_file.name
|
||||
output_file.close()
|
||||
|
||||
ok = _cycles.osl_compile(input_path, output_path)
|
||||
|
||||
if ok:
|
||||
report({'INFO'}, "OSL shader compilation succeeded")
|
||||
|
||||
return ok, output_path
|
||||
|
||||
|
||||
def shader_param_type_default(param, is_bool):
|
||||
if param.isclosure:
|
||||
return 'NodeSocketShader', None
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.COLOR:
|
||||
return 'NodeSocketColor', (param.value[0], param.value[1], param.value[2], 1.0)
|
||||
elif param.type.vecsemantics in [
|
||||
param.type.vecsemantics.POINT,
|
||||
param.type.vecsemantics.VECTOR,
|
||||
param.type.vecsemantics.NORMAL,
|
||||
]:
|
||||
return 'NodeSocketVector', param.value
|
||||
elif param.type.aggregate == param.type.aggregate.SCALAR:
|
||||
if param.type.basetype == param.type.basetype.INT:
|
||||
if is_bool:
|
||||
return 'NodeSocketBool', bool(param.value)
|
||||
else:
|
||||
return 'NodeSocketInt', int(param.value)
|
||||
elif param.type.basetype == param.type.basetype.FLOAT:
|
||||
return 'NodeSocketFloat', float(param.value)
|
||||
elif param.type.basetype == param.type.basetype.STRING:
|
||||
return 'NodeSocketString', str(param.value)
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def shader_param_ensure(node, param):
|
||||
# Skip unsupported types
|
||||
if param.varlenarray or param.isstruct or param.type.arraylen > 1:
|
||||
return None
|
||||
|
||||
metadata = {meta.name: meta.value for meta in param.metadata}
|
||||
|
||||
is_bool = metadata.get('widget') in ['boolean', 'checkBox']
|
||||
hide_value = (param.value is None) or (metadata.get('widget') == 'null')
|
||||
label = metadata.get('label', param.name)
|
||||
|
||||
socket_type, default = shader_param_type_default(param, is_bool)
|
||||
if not socket_type:
|
||||
return None
|
||||
|
||||
sockets = node.outputs if param.isoutput else node.inputs
|
||||
if param.name in sockets:
|
||||
sock = sockets[param.name]
|
||||
|
||||
if sock.bl_idname != socket_type:
|
||||
# Type doesn't match, delete the socket and recreate it below
|
||||
sockets.remove(sock)
|
||||
else:
|
||||
# Update properties if needed
|
||||
if sock.name != label:
|
||||
sock.name = label
|
||||
if not param.isoutput and sock.hide_value != hide_value:
|
||||
sock.hide_value = hide_value
|
||||
|
||||
# We have a matching socket, no need to create one
|
||||
return sock
|
||||
|
||||
sock = sockets.new(type=socket_type, name=label, identifier=param.name)
|
||||
if default is not None:
|
||||
sock.default_value = default
|
||||
sock.hide_value = hide_value
|
||||
return sock
|
||||
|
||||
|
||||
def osl_param_ensure_property(ccam, param):
|
||||
import idprop
|
||||
|
||||
if param.isoutput or param.isclosure:
|
||||
return None
|
||||
|
||||
# Get metadata for the parameter to control UI display
|
||||
metadata = {meta.name: meta.value for meta in param.metadata}
|
||||
if 'label' not in metadata:
|
||||
metadata['label'] = param.name
|
||||
|
||||
datatype = None
|
||||
if param.type.basetype == param.type.basetype.INT:
|
||||
datatype = int
|
||||
elif param.type.basetype == param.type.basetype.FLOAT:
|
||||
datatype = float
|
||||
elif param.type.basetype == param.type.basetype.STRING:
|
||||
datatype = str
|
||||
|
||||
# OSl doesn't have boolean as a type, but we do
|
||||
if (datatype == int) and (metadata.get('widget') in ('boolean', 'checkBox')):
|
||||
datatype = bool
|
||||
default = param.value if isinstance(param.value, tuple) else [param.value]
|
||||
default = [datatype(v) for v in default]
|
||||
|
||||
name = param.name
|
||||
if name in ccam:
|
||||
# If the parameter already exists, only reset its value if its type
|
||||
# or array length changed
|
||||
cur_data = ccam[name]
|
||||
if isinstance(cur_data, idprop.types.IDPropertyArray):
|
||||
cur_length = len(cur_data)
|
||||
cur_type = type(cur_data[0])
|
||||
else:
|
||||
cur_length = 1
|
||||
cur_type = type(cur_data)
|
||||
do_replace = datatype != cur_type or len(default) != cur_length
|
||||
else:
|
||||
# Parameter doesn't exist yet, so set it from the defaults
|
||||
do_replace = True
|
||||
|
||||
if do_replace:
|
||||
ccam[name] = tuple(default) if len(default) > 1 else default[0]
|
||||
|
||||
ui = ccam.id_properties_ui(name)
|
||||
ui.clear()
|
||||
ui.update(default=tuple(default) if len(default) > 1 else default[0])
|
||||
|
||||
# Determine subtype (limited unit support for now)
|
||||
if param.type.vecsemantics == param.type.vecsemantics.COLOR:
|
||||
ui.update(subtype='COLOR')
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.POINT:
|
||||
ui.update(subtype='TRANSLATION')
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.NORMAL:
|
||||
ui.update(subtype='DIRECTION')
|
||||
elif datatype is str and metadata.get('widget') == 'filename':
|
||||
ui.update(subtype='FILE_PATH')
|
||||
elif datatype is float and metadata.get('unit') == 'radians':
|
||||
ui.update(subtype='ANGLE')
|
||||
elif datatype is float and metadata.get('unit') == 'm':
|
||||
ui.update(subtype='DISTANCE')
|
||||
elif datatype is float and metadata.get('unit') == 'mm':
|
||||
ui.update(subtype='DISTANCE_CAMERA')
|
||||
elif datatype is float and metadata.get('unit') in ('s', 'sec'):
|
||||
ui.update(subtype='TIME_ABSOLUTE')
|
||||
elif metadata.get('slider'):
|
||||
ui.update(subtype='FACTOR')
|
||||
elif datatype is int and metadata.get('widget') == 'mapper':
|
||||
options = metadata.get('options', "")
|
||||
options = options.split("|")
|
||||
option_items = []
|
||||
for option in options:
|
||||
if ":" not in option:
|
||||
continue
|
||||
item, index = option.split(":")
|
||||
# Ensure that the index can be converted to an integer
|
||||
try:
|
||||
int(index)
|
||||
except ValueError:
|
||||
continue
|
||||
option_items.append((str(index), bpy.path.display_name(item), ""))
|
||||
ui.update(items=option_items)
|
||||
|
||||
# Map OSL metadata to Blender names
|
||||
option_map = {
|
||||
'help': 'description',
|
||||
'sensitivity': 'step', 'digits': 'precision',
|
||||
'min': 'min', 'max': 'max',
|
||||
'slidermin': 'soft_min', 'slidermax': 'soft_max',
|
||||
}
|
||||
if 'sensitivity' in metadata:
|
||||
# Blender divides this value by 100 by convention, so counteract that.
|
||||
metadata['sensitivity'] *= 100
|
||||
for option, value in metadata.items():
|
||||
if option in option_map:
|
||||
ui.update(**{option_map[option]: value})
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def update_external_script(report, filepath, library):
|
||||
"""compile and update OSL script"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
script_path = bpy.path.abspath(filepath, library=library)
|
||||
script_path_noext, script_ext = os.path.splitext(script_path)
|
||||
|
||||
if script_ext == ".oso":
|
||||
# it's a .oso file, no need to compile
|
||||
ok, oso_path = True, script_path
|
||||
elif script_ext == ".osl":
|
||||
# compile .osl file
|
||||
ok, oso_path = osl_compile(script_path, report)
|
||||
oso_file_remove = True
|
||||
|
||||
if ok:
|
||||
# copy .oso from temporary path to .osl directory
|
||||
dst_path = script_path_noext + ".oso"
|
||||
try:
|
||||
shutil.copy2(oso_path, dst_path)
|
||||
except:
|
||||
report({'ERROR'}, rpt_("Failed to write .oso file next to external .osl file at {:s}").format(dst_path))
|
||||
elif os.path.dirname(filepath) == "":
|
||||
# module in search path
|
||||
oso_path = filepath
|
||||
ok = True
|
||||
else:
|
||||
# unknown
|
||||
report({'ERROR'}, "External shader script must have .osl or .oso extension, or be a module name")
|
||||
ok = False
|
||||
|
||||
return ok, oso_path, oso_file_remove
|
||||
|
||||
|
||||
def update_internal_script(report, script):
|
||||
"""compile and update shader script node"""
|
||||
import os
|
||||
import tempfile
|
||||
import pathlib
|
||||
import hashlib
|
||||
|
||||
bytecode = None
|
||||
bytecode_hash = None
|
||||
|
||||
osl_path = bpy.path.abspath(script.filepath, library=script.library)
|
||||
|
||||
if script.is_in_memory or script.is_dirty or script.is_modified or not os.path.exists(osl_path):
|
||||
# write text datablock contents to temporary file
|
||||
osl_file = tempfile.NamedTemporaryFile(mode='w', suffix=".osl", delete=False)
|
||||
osl_file.write(script.as_string())
|
||||
osl_file.write("\n")
|
||||
osl_file.close()
|
||||
|
||||
ok, oso_path = osl_compile(osl_file.name, report)
|
||||
os.remove(osl_file.name)
|
||||
else:
|
||||
# compile text datablock from disk directly
|
||||
ok, oso_path = osl_compile(osl_path, report)
|
||||
|
||||
if ok:
|
||||
# read bytecode
|
||||
try:
|
||||
bytecode = pathlib.Path(oso_path).read_text()
|
||||
md5 = hashlib.md5(usedforsecurity=False)
|
||||
md5.update(bytecode.encode())
|
||||
bytecode_hash = md5.hexdigest()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
report({'ERROR'}, "Cannot read OSO bytecode to store in node at {!r}".format(oso_path))
|
||||
ok = False
|
||||
|
||||
return ok, oso_path, bytecode, bytecode_hash
|
||||
|
||||
|
||||
def update_script_node(node, report):
|
||||
"""compile and update shader script node"""
|
||||
import os
|
||||
import oslquery
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
if node.mode == 'EXTERNAL':
|
||||
# compile external script file
|
||||
ok, oso_path, oso_file_remove = update_external_script(report, node.filepath, node.id_data.library)
|
||||
if ok:
|
||||
# Clear old internal bytecode, and also trigger node update if it was already cleared.
|
||||
node.bytecode = ""
|
||||
node.bytecode_hash = ""
|
||||
|
||||
elif node.mode == 'INTERNAL' and node.script:
|
||||
# internal script, we will store bytecode in the node
|
||||
ok, oso_path, bytecode, bytecode_hash = update_internal_script(report, node.script)
|
||||
if bytecode:
|
||||
node.bytecode = bytecode
|
||||
node.bytecode_hash = bytecode_hash
|
||||
|
||||
else:
|
||||
report({'WARNING'}, "No text or file specified in node, nothing to compile")
|
||||
return
|
||||
|
||||
if ok:
|
||||
if query := oslquery.OSLQuery(oso_path):
|
||||
# Ensure that all parameters have a matching socket
|
||||
used_sockets = set()
|
||||
for param in query.parameters:
|
||||
if sock := shader_param_ensure(node, param):
|
||||
used_sockets.add(sock)
|
||||
|
||||
# Remove unused sockets
|
||||
for sockets in (node.inputs, node.outputs):
|
||||
for identifier in [sock.identifier for sock in sockets]:
|
||||
if sockets[identifier] not in used_sockets:
|
||||
sockets.remove(sockets[identifier])
|
||||
else:
|
||||
ok = False
|
||||
report({'ERROR'}, rpt_("OSL query failed to open %s") % oso_path)
|
||||
else:
|
||||
report({'ERROR'}, "OSL script compilation failed, see console for errors")
|
||||
|
||||
# remove temporary oso file
|
||||
if oso_file_remove:
|
||||
try:
|
||||
os.remove(oso_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
def update_custom_camera_shader(cam, report):
|
||||
"""compile and update custom camera shader"""
|
||||
import os
|
||||
import oslquery
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
custom_props = cam.cycles_custom
|
||||
if cam.custom_mode == 'EXTERNAL':
|
||||
# compile external script file
|
||||
ok, oso_path, oso_file_remove = update_external_script(report, cam.custom_filepath, cam.library)
|
||||
|
||||
elif cam.custom_mode == 'INTERNAL' and cam.custom_shader:
|
||||
# internal script, we will store bytecode in the node
|
||||
ok, oso_path, bytecode, bytecode_hash = update_internal_script(report, cam.custom_shader)
|
||||
if bytecode:
|
||||
cam.custom_bytecode = bytecode
|
||||
cam.custom_bytecode_hash = bytecode_hash
|
||||
cam.update_tag()
|
||||
|
||||
else:
|
||||
report({'WARNING'}, "No text or file specified in node, nothing to compile")
|
||||
return
|
||||
|
||||
if ok:
|
||||
if query := oslquery.OSLQuery(oso_path):
|
||||
# Ensure that all parameters have a matching property
|
||||
used_params = set()
|
||||
for param in query.parameters:
|
||||
if name := osl_param_ensure_property(custom_props, param):
|
||||
used_params.add(name)
|
||||
|
||||
# Clean up unused parameters
|
||||
for prop in list(custom_props.keys()):
|
||||
if prop not in used_params:
|
||||
del custom_props[prop]
|
||||
else:
|
||||
ok = False
|
||||
report({'ERROR'}, rpt_("OSL query failed to open %s") % oso_path)
|
||||
else:
|
||||
report({'ERROR'}, "Custom Camera shader compilation failed, see console for errors")
|
||||
|
||||
# remove temporary oso file
|
||||
if oso_file_remove:
|
||||
try:
|
||||
os.remove(oso_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return ok
|
||||
136
blender-5.2.0/intern/cycles/blender/addon/presets.py
Normal file
136
blender-5.2.0/intern/cycles/blender/addon/presets.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from bl_operators.presets import AddPresetBase
|
||||
from bpy.types import Operator
|
||||
|
||||
|
||||
class AddPresetIntegrator(AddPresetBase, Operator):
|
||||
'''Add an Integrator Preset'''
|
||||
bl_idname = "render.cycles_integrator_preset_add"
|
||||
bl_label = "Add Integrator Preset"
|
||||
preset_menu = "CYCLES_PT_integrator_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.max_bounces",
|
||||
"cycles.diffuse_bounces",
|
||||
"cycles.glossy_bounces",
|
||||
"cycles.transmission_bounces",
|
||||
"cycles.volume_bounces",
|
||||
"cycles.transparent_max_bounces",
|
||||
"cycles.caustics_reflective",
|
||||
"cycles.caustics_refractive",
|
||||
"cycles.blur_glossy",
|
||||
"cycles.use_fast_gi",
|
||||
"cycles.ao_bounces",
|
||||
"cycles.ao_bounces_render",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/integrator"
|
||||
|
||||
|
||||
class AddPresetSampling(AddPresetBase, Operator):
|
||||
'''Add a Sampling Preset'''
|
||||
bl_idname = "render.cycles_sampling_preset_add"
|
||||
bl_label = "Add Sampling Preset"
|
||||
preset_menu = "CYCLES_PT_sampling_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.use_adaptive_sampling",
|
||||
"cycles.samples",
|
||||
"cycles.adaptive_threshold",
|
||||
"cycles.adaptive_min_samples",
|
||||
"cycles.time_limit",
|
||||
"cycles.use_denoising",
|
||||
"cycles.denoiser",
|
||||
"cycles.denoising_input_passes",
|
||||
"cycles.denoising_prefilter",
|
||||
"cycles.denoising_quality",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/sampling"
|
||||
|
||||
|
||||
class AddPresetViewportSampling(AddPresetBase, Operator):
|
||||
'''Add a Viewport Sampling Preset'''
|
||||
bl_idname = "render.cycles_viewport_sampling_preset_add"
|
||||
bl_label = "Add Viewport Sampling Preset"
|
||||
preset_menu = "CYCLES_PT_viewport_sampling_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.use_preview_adaptive_sampling",
|
||||
"cycles.preview_samples",
|
||||
"cycles.preview_adaptive_threshold",
|
||||
"cycles.preview_adaptive_min_samples",
|
||||
"cycles.use_preview_denoising",
|
||||
"cycles.preview_denoiser",
|
||||
"cycles.preview_denoising_input_passes",
|
||||
"cycles.preview_denoising_prefilter",
|
||||
"cycles.preview_denoising_quality",
|
||||
"cycles.preview_denoising_start_sample",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/viewport_sampling"
|
||||
|
||||
|
||||
class AddPresetPerformance(AddPresetBase, Operator):
|
||||
'''Add an Performance Preset'''
|
||||
bl_idname = "render.cycles_performance_preset_add"
|
||||
bl_label = "Add Performance Preset"
|
||||
preset_menu = "CYCLES_PT_performance_presets"
|
||||
|
||||
preset_defines = [
|
||||
"render = bpy.context.scene.render",
|
||||
"cycles = bpy.context.scene.cycles",
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"render.threads_mode",
|
||||
"render.use_persistent_data",
|
||||
"cycles.debug_use_spatial_splits",
|
||||
"cycles.debug_use_compact_bvh",
|
||||
"cycles.debug_use_hair_bvh",
|
||||
"cycles.debug_bvh_time_steps",
|
||||
"cycles.tile_size",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/performance"
|
||||
|
||||
|
||||
classes = (
|
||||
AddPresetIntegrator,
|
||||
AddPresetSampling,
|
||||
AddPresetViewportSampling,
|
||||
AddPresetPerformance,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
2063
blender-5.2.0/intern/cycles/blender/addon/properties.py
Normal file
2063
blender-5.2.0/intern/cycles/blender/addon/properties.py
Normal file
File diff suppressed because it is too large
Load Diff
2708
blender-5.2.0/intern/cycles/blender/addon/ui.py
Normal file
2708
blender-5.2.0/intern/cycles/blender/addon/ui.py
Normal file
File diff suppressed because it is too large
Load Diff
330
blender-5.2.0/intern/cycles/blender/addon/version_update.py
Normal file
330
blender-5.2.0/intern/cycles/blender/addon/version_update.py
Normal file
@@ -0,0 +1,330 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
def custom_bake_remap(scene):
|
||||
"""
|
||||
Remap bake types into the new types and set the flags accordingly
|
||||
"""
|
||||
bake_lookup = (
|
||||
'COMBINED',
|
||||
'AO',
|
||||
'SHADOW',
|
||||
'NORMAL',
|
||||
'UV',
|
||||
'EMIT',
|
||||
'ENVIRONMENT',
|
||||
'DIFFUSE_DIRECT',
|
||||
'DIFFUSE_INDIRECT',
|
||||
'DIFFUSE_COLOR',
|
||||
'GLOSSY_DIRECT',
|
||||
'GLOSSY_INDIRECT',
|
||||
'GLOSSY_COLOR',
|
||||
'TRANSMISSION_DIRECT',
|
||||
'TRANSMISSION_INDIRECT',
|
||||
'TRANSMISSION_COLOR')
|
||||
|
||||
diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
|
||||
|
||||
cscene = scene.cycles
|
||||
|
||||
# Old bake type
|
||||
bake_type_idx = cscene.get("bake_type")
|
||||
|
||||
if bake_type_idx is None:
|
||||
cscene.bake_type = 'COMBINED'
|
||||
return
|
||||
|
||||
# File doesn't need versioning
|
||||
if bake_type_idx < diffuse_direct_idx:
|
||||
return
|
||||
|
||||
# File needs versioning
|
||||
bake_type = bake_lookup[bake_type_idx]
|
||||
cscene.bake_type, end = bake_type.split('_')
|
||||
|
||||
if end == 'DIRECT':
|
||||
scene.render.bake.use_pass_indirect = False
|
||||
scene.render.bake.use_pass_color = False
|
||||
|
||||
elif end == 'INDIRECT':
|
||||
scene.render.bake.use_pass_direct = False
|
||||
scene.render.bake.use_pass_color = False
|
||||
|
||||
elif end == 'COLOR':
|
||||
scene.render.bake.use_pass_direct = False
|
||||
scene.render.bake.use_pass_indirect = False
|
||||
|
||||
|
||||
@persistent
|
||||
def do_versions(self):
|
||||
if bpy.context.preferences.version <= (2, 78, 1):
|
||||
prop = bpy.context.preferences.addons[__package__].preferences
|
||||
system = bpy.context.preferences.system
|
||||
if not prop.is_property_set("compute_device_type"):
|
||||
# Device might not currently be available so this can fail
|
||||
try:
|
||||
if system.legacy_compute_device_type == 1:
|
||||
prop.compute_device_type = 'NONE' # Was OpenCL
|
||||
elif system.legacy_compute_device_type == 2:
|
||||
prop.compute_device_type = 'CUDA'
|
||||
else:
|
||||
prop.compute_device_type = 'NONE'
|
||||
except:
|
||||
pass
|
||||
|
||||
# Init device list for UI
|
||||
prop.get_devices(prop.compute_device_type)
|
||||
|
||||
if bpy.context.preferences.version <= (3, 0, 40):
|
||||
# Disable OpenCL device
|
||||
prop = bpy.context.preferences.addons[__package__].preferences
|
||||
if prop.is_property_set("compute_device_type") and prop['compute_device_type'] == 4:
|
||||
prop.compute_device_type = 'NONE'
|
||||
|
||||
# We don't modify startup file because it assumes to
|
||||
# have all the default values only.
|
||||
if not bpy.data.is_saved:
|
||||
return
|
||||
|
||||
# Map of versions used by libraries.
|
||||
library_versions = {}
|
||||
library_versions[bpy.data.version] = [None]
|
||||
for library in bpy.data.libraries:
|
||||
library_versions.setdefault(library.version, []).append(library)
|
||||
|
||||
# Do versioning per library, since they might have different versions.
|
||||
max_need_versioning = (5, 2, 8)
|
||||
for version, libraries in library_versions.items():
|
||||
if version > max_need_versioning:
|
||||
continue
|
||||
|
||||
# Scenes
|
||||
for scene in bpy.data.scenes:
|
||||
if scene.library not in libraries:
|
||||
continue
|
||||
|
||||
# Auto tiling is always enabled now
|
||||
if version <= (5, 0, 77):
|
||||
cscene = scene.cycles
|
||||
if not cscene.use_auto_tile:
|
||||
cscene.use_auto_tile = True
|
||||
cscene.tile_size = 8192
|
||||
|
||||
# Clamp Direct/Indirect separation in 270
|
||||
if version <= (2, 70, 0):
|
||||
cscene = scene.cycles
|
||||
sample_clamp = cscene.get("sample_clamp", False)
|
||||
if (sample_clamp and
|
||||
not cscene.is_property_set("sample_clamp_direct") and
|
||||
not cscene.is_property_set("sample_clamp_indirect")):
|
||||
cscene.sample_clamp_direct = sample_clamp
|
||||
cscene.sample_clamp_indirect = sample_clamp
|
||||
|
||||
# Change of Volume Bounces in 271
|
||||
if version <= (2, 71, 0):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("volume_bounces"):
|
||||
cscene.volume_bounces = 1
|
||||
|
||||
# Caustics Reflective/Refractive separation in 272
|
||||
if version <= (2, 72, 0):
|
||||
cscene = scene.cycles
|
||||
if (
|
||||
cscene.get("no_caustics", False) and
|
||||
not cscene.is_property_set("caustics_reflective") and
|
||||
not cscene.is_property_set("caustics_refractive")
|
||||
):
|
||||
cscene.caustics_reflective = False
|
||||
cscene.caustics_refractive = False
|
||||
|
||||
# Baking types changed
|
||||
if version <= (2, 76, 6):
|
||||
custom_bake_remap(scene)
|
||||
|
||||
# Several default changes for 2.77
|
||||
if version <= (2, 76, 8):
|
||||
cscene = scene.cycles
|
||||
|
||||
# Samples
|
||||
if not cscene.is_property_set("samples"):
|
||||
cscene.samples = 10
|
||||
|
||||
# Preview Samples
|
||||
if not cscene.is_property_set("preview_samples"):
|
||||
cscene.preview_samples = 10
|
||||
|
||||
# Filter
|
||||
if cscene.get("filter_type", -1) == -1:
|
||||
cscene.pixel_filter_type = 'GAUSSIAN'
|
||||
|
||||
if version <= (2, 76, 10):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("pixel_filter_type"):
|
||||
filter_type_int = cscene.get("filter_type", -1)
|
||||
if filter_type_int == 0:
|
||||
cscene.pixel_filter_type = 'BOX'
|
||||
elif filter_type_int == 1:
|
||||
cscene.pixel_filter_type = 'GAUSSIAN'
|
||||
|
||||
if version <= (2, 78, 2):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("light_sampling_threshold"):
|
||||
cscene.light_sampling_threshold = 0.0
|
||||
|
||||
if version <= (2, 79, 0):
|
||||
cscene = scene.cycles
|
||||
# Default changes
|
||||
if not cscene.is_property_set("blur_glossy"):
|
||||
cscene.blur_glossy = 0.0
|
||||
if not cscene.is_property_set("sample_clamp_indirect"):
|
||||
cscene.sample_clamp_indirect = 0.0
|
||||
|
||||
if version <= (2, 92, 4):
|
||||
if scene.render.engine == 'CYCLES':
|
||||
for view_layer in scene.view_layers:
|
||||
cview_layer = view_layer.cycles
|
||||
view_layer.use_pass_cryptomatte_object = cview_layer.get("use_pass_crypto_object", False)
|
||||
view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False)
|
||||
view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False)
|
||||
view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6)
|
||||
|
||||
if version <= (2, 93, 7):
|
||||
if scene.render.engine == 'CYCLES':
|
||||
for view_layer in scene.view_layers:
|
||||
cview_layer = view_layer.cycles
|
||||
for caov in cview_layer.get("aovs", []):
|
||||
aov_name = caov.get("name", "AOV")
|
||||
if aov_name in view_layer.aovs:
|
||||
continue
|
||||
baov = view_layer.aovs.add()
|
||||
baov.name = caov.get("name", "AOV")
|
||||
baov.type = "COLOR" if caov.get("type", 1) == 1 else "VALUE"
|
||||
|
||||
if version <= (2, 93, 16):
|
||||
cscene = scene.cycles
|
||||
ao_bounces = cscene.get("ao_bounces", 0)
|
||||
ao_bounces_render = cscene.get("ao_bounces_render", 0)
|
||||
if scene.render.use_simplify and (ao_bounces or ao_bounces_render):
|
||||
cscene.use_fast_gi = True
|
||||
cscene.ao_bounces = ao_bounces
|
||||
cscene.ao_bounces_render = ao_bounces_render
|
||||
else:
|
||||
cscene.ao_bounces = 1
|
||||
cscene.ao_bounces_render = 1
|
||||
|
||||
if version <= (3, 0, 25):
|
||||
cscene = scene.cycles
|
||||
|
||||
# Default changes.
|
||||
if not cscene.is_property_set("samples"):
|
||||
cscene.samples = 128
|
||||
if not cscene.is_property_set("preview_samples"):
|
||||
cscene.preview_samples = 32
|
||||
if not cscene.is_property_set("use_adaptive_sampling"):
|
||||
cscene.use_adaptive_sampling = False
|
||||
cscene.use_preview_adaptive_sampling = False
|
||||
if not cscene.is_property_set("use_denoising"):
|
||||
cscene.use_denoising = False
|
||||
if not cscene.is_property_set("use_preview_denoising"):
|
||||
cscene.use_preview_denoising = False
|
||||
if not cscene.is_property_set("sampling_pattern") or \
|
||||
cscene.get('sampling_pattern') >= 2:
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
# Removal of square samples.
|
||||
cscene = scene.cycles
|
||||
use_square_samples = cscene.get("use_square_samples", False)
|
||||
|
||||
if use_square_samples:
|
||||
cscene.samples *= cscene.samples
|
||||
cscene.preview_samples *= cscene.preview_samples
|
||||
for layer in scene.view_layers:
|
||||
layer.samples *= layer.samples
|
||||
cscene["use_square_samples"] = False
|
||||
|
||||
if version <= (3, 5, 3):
|
||||
cscene = scene.cycles
|
||||
# Disable light tree for existing scenes.
|
||||
if not cscene.is_property_set("use_light_tree"):
|
||||
cscene.use_light_tree = False
|
||||
# Sampling pattern settings are hidden behind a debug menu. Switch to the
|
||||
# default faster and fully featured (Supports Scrambling Distance)
|
||||
# Tabulated Sobol.
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
if version <= (4, 2, 52):
|
||||
cscene = scene.cycles
|
||||
# Previous versions defaulted to Tabulated Sobol unless debugging options
|
||||
# were enabled, so keep this behavior instead of suddenly defaulting to
|
||||
# blue noise if the file happens to contain a different option for the enum.
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
# Lamps
|
||||
for light in bpy.data.lights:
|
||||
if light.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 5):
|
||||
clight = light.cycles
|
||||
|
||||
# MIS
|
||||
if not clight.is_property_set("use_multiple_importance_sampling"):
|
||||
clight.use_multiple_importance_sampling = False
|
||||
|
||||
# Worlds
|
||||
for world in bpy.data.worlds:
|
||||
if world.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 9):
|
||||
cworld = world.cycles
|
||||
|
||||
# World MIS Resolution
|
||||
if not cworld.is_property_set("sample_map_resolution"):
|
||||
cworld.sample_map_resolution = 256
|
||||
|
||||
if version <= (2, 79, 4) or \
|
||||
(version >= (2, 80, 0) and version <= (2, 80, 18)):
|
||||
cworld = world.cycles
|
||||
# World MIS
|
||||
if not cworld.is_property_set("sampling_method"):
|
||||
if cworld.get("sample_as_light", True):
|
||||
cworld.sampling_method = 'MANUAL'
|
||||
else:
|
||||
cworld.sampling_method = 'NONE'
|
||||
|
||||
# Materials
|
||||
for mat in bpy.data.materials:
|
||||
if mat.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 5):
|
||||
cmat = mat.cycles
|
||||
# Volume Sampling
|
||||
if not cmat.is_property_set("volume_sampling"):
|
||||
cmat.volume_sampling = 'DISTANCE'
|
||||
|
||||
if version <= (2, 79, 2):
|
||||
cmat = mat.cycles
|
||||
if cmat.get("displacement_method", -1) == -1:
|
||||
cmat['displacement_method'] = 0
|
||||
|
||||
# Change default to bump again.
|
||||
if version <= (2, 79, 6) or \
|
||||
(version >= (2, 80, 0) and version <= (2, 80, 41)):
|
||||
cmat = mat.cycles
|
||||
if cmat.get("displacement_method", -1) == -1:
|
||||
cmat['displacement_method'] = 1
|
||||
|
||||
if version <= (3, 5, 3):
|
||||
cmat = mat.cycles
|
||||
if not cmat.get("sample_as_light", True):
|
||||
cmat.emission_sampling = 'NONE'
|
||||
Reference in New Issue
Block a user