Last active
March 22, 2021 23:40
-
-
Save unwave/5434baf1ac8a5b7ec6fdf28d1c4936fe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import bpy | |
| #import os | |
| #os.system('cls') | |
| class Position_Material_Override: | |
| def __init__(self, objects): | |
| self.objects = objects | |
| def __enter__(self): | |
| position_material = bpy.data.materials.new(name="__position__") | |
| position_material.use_nodes = True | |
| self.position_material = position_material | |
| nodes = position_material.node_tree.nodes | |
| links = position_material.node_tree.links | |
| output = nodes["Material Output"] | |
| emission = nodes.new(type='ShaderNodeEmission') | |
| geometry = nodes.new(type='ShaderNodeNewGeometry') | |
| links.new(geometry.outputs['Position'], emission.inputs['Color']) | |
| links.new(emission.outputs['Emission'], output.inputs['Surface']) | |
| prev_materials = {} | |
| self.prev_materials = prev_materials | |
| for object in self.objects: | |
| slots = object.material_slots | |
| if not slots: | |
| prev_materials[object] = None | |
| object.data.materials.append(position_material) | |
| else: | |
| prev_materials[object] = [] | |
| for slot in slots: | |
| if slot.material: | |
| prev_materials[object].append(slot.material) | |
| else: | |
| prev_materials[object].append(None) | |
| slot.material = position_material | |
| def __exit__(self, type, value, traceback): | |
| prev_materials = self.prev_materials | |
| for object in self.objects: | |
| materials = prev_materials[object] | |
| if prev_materials[object] == None: | |
| object.data.materials.clear() | |
| else: | |
| slots = object.material_slots | |
| for index, material in enumerate(materials): | |
| slots[index].material = material | |
| bpy.data.materials.remove(self.position_material) | |
| class Height_Baking: | |
| def __init__(self, object): | |
| self.object = object | |
| def __enter__(self): | |
| object = self.object | |
| self.prev_cycles_samples = bpy.context.scene.cycles.samples | |
| bpy.context.scene.cycles.samples = 1 | |
| baking_material = bpy.data.materials.new(name="__height_baking__") | |
| baking_material.use_nodes = True | |
| self.baking_material = baking_material | |
| self.prev_materials = None | |
| slots = self.object.material_slots | |
| if not slots: | |
| object.data.materials.append(position_material) | |
| else: | |
| self.prev_materials = [] | |
| for slot in slots: | |
| if slot.material: | |
| self.prev_materials.append(slot.material) | |
| else: | |
| self.prev_materials.append(None) | |
| slot.material = baking_material | |
| nodes = baking_material.node_tree.nodes | |
| self.nodes = nodes | |
| links = baking_material.node_tree.links | |
| self.links = links | |
| position = nodes.new(type='ShaderNodeTexImage') | |
| self.position_node = position | |
| output = nodes["Material Output"] | |
| geometry = nodes.new(type='ShaderNodeNewGeometry') | |
| emission = nodes.new(type='ShaderNodeEmission') | |
| subtract = nodes.new(type='ShaderNodeMixRGB') | |
| subtract.blend_type = 'SUBTRACT' | |
| add = nodes.new(type='ShaderNodeMath') | |
| add.operation = 'ADD' | |
| dot_product = nodes.new(type='ShaderNodeVectorMath') | |
| dot_product.operation = 'DOT_PRODUCT' | |
| map_range = nodes.new(type='ShaderNodeMapRange') | |
| self.map_range = map_range | |
| map_range.clamp = False | |
| links.new(position.outputs['Alpha'], subtract.inputs['Fac']) | |
| links.new(position.outputs['Color'], subtract.inputs['Color1']) | |
| links.new(geometry.outputs['Position'], subtract.inputs['Color2']) | |
| links.new(geometry.outputs['Normal'], dot_product.inputs[0]) | |
| links.new(subtract.outputs['Color'], dot_product.inputs[1]) | |
| links.new(dot_product.outputs['Value'], add.inputs[0]) | |
| links.new(add.outputs['Value'], map_range.inputs['Value']) | |
| links.new(map_range.outputs['Result'], emission.inputs['Color']) | |
| links.new(emission.outputs['Emission'], output.inputs['Surface']) | |
| return self | |
| def __exit__(self, type, value, traceback): | |
| bpy.data.images.remove(self.position_image) | |
| bpy.data.materials.remove(self.baking_material) | |
| bpy.context.scene.cycles.samples = self.prev_cycles_samples | |
| if self.prev_materials == None: | |
| self.object.data.materials.clear() | |
| else: | |
| slots = self.object.material_slots | |
| for index, material in enumerate(self.prev_materials): | |
| slots[index].material = material | |
| def bake(self, width, height, cage_extrusion = 0.5, float_buffer = False, margin=0): | |
| # for GPU? | |
| #self.prev_render_tile_x = bpy.context.scene.render.tile_x | |
| #self.prev_render_tile_y = bpy.context.scene.render.tile_y | |
| #bpy.context.scene.render.tile_x = 256 | |
| #bpy.context.scene.render.tile_y = 256 | |
| use_persistent_data = bpy.context.scene.render.use_persistent_data | |
| bpy.context.scene.render.use_persistent_data = False # otherwise will fail | |
| self.position_image = bpy.data.images.new("__baked_position__", width=width, height=height, float_buffer=True, is_data=True) | |
| self.position_node.image = self.position_image | |
| self.position_node.select = True | |
| self.nodes.active = self.position_node | |
| bpy.ops.object.bake(type='EMIT', cage_extrusion=cage_extrusion, use_selected_to_active=True, margin=margin) | |
| height_image = bpy.data.images.new(self.object.name + "_height", width=width, height=height, float_buffer=float_buffer, is_data=True) | |
| height = self.nodes.new( type='ShaderNodeTexImage') | |
| height.image = height_image | |
| height.select = True | |
| self.nodes.active = height | |
| cage_extrusion = cage_extrusion/2 | |
| self.map_range.inputs['From Min'].default_value = -(cage_extrusion - 0.5) | |
| self.map_range.inputs['From Max'].default_value = cage_extrusion + 0.5 | |
| bpy.ops.object.bake({'selected_objects': [self.object]}, use_selected_to_active=False, type='EMIT', margin=margin) | |
| #bpy.context.scene.render.tile_x = self.prev_render_tile_x | |
| #bpy.context.scene.render.tile_y = self.prev_render_tile_y | |
| bpy.context.scene.render.use_persistent_data = use_persistent_data | |
| return height_image | |
| selected_objects = bpy.context.selected_objects | |
| assert len(selected_objects) > 1, "Select at least two objects." | |
| active = bpy.context.object | |
| selected = selected_objects.copy() | |
| selected.remove(active) | |
| from timeit import default_timer as timer | |
| with Position_Material_Override(selected), Height_Baking(active) as baking: | |
| start = timer() | |
| cage_extrusion = 3 | |
| image = baking.bake(1024, 1024, cage_extrusion = cage_extrusion, float_buffer = False, margin=0) | |
| print("Baking time:", timer()-start) | |
| nodes = bpy.data.materials["TEST"].node_tree.nodes | |
| nodes["Image Texture"].image = image | |
| nodes["Displacement"].inputs['Scale'].default_value = cage_extrusion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment