2022-12-25 12:10:23 -07:00
|
|
|
import bpy
|
2022-12-25 13:04:00 -07:00
|
|
|
from bpy.types import Context, Object, Scene, Collection, ViewLayer, LayerCollection
|
2022-12-25 12:10:23 -07:00
|
|
|
|
|
|
|
def get_first_collection_sharing_scene(scene: Scene, obj: Object):
|
|
|
|
def collection_in_scene(collection: Collection, tree: Collection):
|
|
|
|
if collection == tree:
|
|
|
|
return True
|
|
|
|
return any(collection_in_scene(collection, child) for child in tree.children)
|
|
|
|
return next(candidate for candidate in obj.users_collection if collection_in_scene(candidate, scene.collection))
|
|
|
|
|
2022-12-25 13:04:00 -07:00
|
|
|
def collection_to_layer_collection(view_layer: ViewLayer, collection: Collection) -> LayerCollection:
|
|
|
|
wl = [view_layer.layer_collection]
|
|
|
|
while len(wl):
|
|
|
|
lc = wl.pop()
|
|
|
|
if lc.collection == collection:
|
|
|
|
return lc
|
|
|
|
wl.extend(lc.children)
|
|
|
|
|
2022-12-25 12:10:23 -07:00
|
|
|
def is_pc2_enabled():
|
|
|
|
return hasattr(bpy.ops, "export_shape") and hasattr(bpy.ops.export_shape, "pc2")
|
|
|
|
|
|
|
|
def export_pc2_filepath(obj: Object, absolute=False):
|
|
|
|
path = f"//{obj.name}.pc2"
|
|
|
|
return bpy.path.abspath(path) if absolute else path
|
|
|
|
|
|
|
|
def export_pc2(scene: Scene, obj: Object):
|
|
|
|
path = export_pc2_filepath(obj, absolute=True)
|
|
|
|
frame_start = scene.frame_start
|
|
|
|
scene.frame_set(frame_start)
|
|
|
|
frame_end = scene.frame_end
|
|
|
|
bpy.context.view_layer.objects.active = obj
|
|
|
|
bpy.context.view_layer.update()
|
|
|
|
bpy.ops.export_shape.pc2(filepath=path, check_existing=False, rot_x90=False, world_space=False, apply_modifiers=True, range_start=frame_start, range_end=frame_end, sampling='1')
|
|
|
|
bpy.context.view_layer.update()
|
|
|
|
frame_end = scene.frame_end
|
|
|
|
scene.frame_set(frame_start)
|
|
|
|
bpy.context.view_layer.update()
|
|
|
|
for modifier in obj.modifiers:
|
|
|
|
bpy.ops.object.modifier_apply(modifier=modifier.name)
|
|
|
|
bpy.context.view_layer.update()
|
|
|
|
|
2022-12-25 13:04:00 -07:00
|
|
|
def create_proxy(view_layer: ViewLayer, scene: Scene, obj: Object):
|
2022-12-25 12:10:23 -07:00
|
|
|
copy = obj.copy()
|
|
|
|
copy.name = f"{obj.name}Bake"
|
|
|
|
copy.data = copy.data.copy()
|
|
|
|
copy.constraints.clear()
|
|
|
|
c = get_first_collection_sharing_scene(scene, obj)
|
|
|
|
c.objects.link(copy)
|
|
|
|
export_pc2(scene, copy)
|
|
|
|
mod = copy.modifiers.new("Mesh Bake", "MESH_CACHE")
|
|
|
|
mod.cache_format = "PC2"
|
2022-12-25 13:04:00 -07:00
|
|
|
mod.filepath = export_pc2_filepath(copy)
|
|
|
|
|
|
|
|
c.objects.unlink(obj)
|
|
|
|
cc = bpy.data.collections.new(f"{obj.name}-unbaked")
|
|
|
|
cc.objects.link(obj)
|
|
|
|
c.children.link(cc)
|
|
|
|
lcc = collection_to_layer_collection(view_layer, cc)
|
|
|
|
lcc.exclude = True
|
2022-12-25 12:10:23 -07:00
|
|
|
|
|
|
|
class BakeMeshOperator(bpy.types.Operator):
|
|
|
|
bl_idname = "object.bakemesh"
|
|
|
|
bl_label = "Bake Mesh to PC2"
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(Cls, context: Context):
|
|
|
|
return context.active_object is not None
|
|
|
|
|
|
|
|
def execute(self, context: Context):
|
|
|
|
if not is_pc2_enabled():
|
|
|
|
self.report({"ERROR"}, "PC2 export addon must be enabled")
|
|
|
|
return {'FINISHED'}
|
|
|
|
scene = context.scene
|
|
|
|
obj = context.active_object
|
2022-12-25 13:04:00 -07:00
|
|
|
create_proxy(context.view_layer, scene, obj)
|
2022-12-25 12:10:23 -07:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
def BakeMeshOperator_menu_func(self, context: Context):
|
|
|
|
return self.layout.operator(BakeMeshOperator.bl_idname)
|
|
|
|
|
|
|
|
def register():
|
|
|
|
bpy.types.VIEW3D_MT_object.append(BakeMeshOperator_menu_func)
|
|
|
|
|
|
|
|
def unregister():
|
|
|
|
bpy.types.VIEW3D_MT_object.remove(BakeMeshOperator_menu_func)
|