├── README.md └── io_scene_xom3d ├── __init__.py └── import_xom3d.py /README.md: -------------------------------------------------------------------------------- 1 | # xom-import 2 | 3 | **xom-import** is a plugin for Blender 3D that allows you to import 3D models 4 | from Worms games (Worms 3D, Worms Ultimate Mayhem, Worms Forts: Under Siege, etc) into the editor. 5 | It works with two file formats: models (`.xom3d`) and animations (`.xac`). 6 | The plugin cannot export loaded models. So you can't use it to make changes to the Worms games. 7 | 8 | ![](https://next21.ru/wp-content/uploads/2018/12/ezgif-2-6272f3db265e.gif) 9 | 10 | ## Requirements 11 | 12 | * Blender 3D (2.79 or 2.80) 13 | * [XomView v3.0](https://www.dropbox.com/s/vawww6wf8xdhq66/xom%20view.zip?dl=0) by AlexBond 14 | 15 | XomView is used to extract resources from the Worms game bundles, including the required `.xom3d` 16 | and `.xac` files. The plugin itself doesn't use XomView. 17 | 18 | ## Importing models 19 | 20 | First you need to get the necessary models, textures and animations using XomView. 21 | After exporting models, check that the textures are located in the same directory 22 | with the model (if it uses them of course). Files with animations can be located anywhere. 23 | 24 | Then simply import the model into Blender 3D. 25 | 26 | ![](https://next21.ru/wp-content/uploads/2018/12/ezgif-2-bf3dcae8a8e7.gif) 27 | 28 | To load `.xac` file, make armature active and import the animation you want. 29 | 30 | Some animations require a Base pose. To properly import such animations, you must first load 31 | the data from the file named `[Base].xac`. To do this, simply import `[Base].xac` as an animation. 32 | After that, you can import other animations. 33 | 34 | ![](https://next21.ru/wp-content/uploads/2018/12/ezgif-2-df30e3146c68.gif) 35 | 36 | Optionally, you can uncheck "Reset pose" to keep the previous position before loading the animation. 37 | 38 | ![](https://next21.ru/wp-content/uploads/2018/12/ezgif-2-e37a18c7ec37.gif) 39 | -------------------------------------------------------------------------------- /io_scene_xom3d/__init__.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import ( 3 | BoolProperty, 4 | StringProperty, 5 | FloatVectorProperty, 6 | CollectionProperty, 7 | ) 8 | from bpy.types import ( 9 | PoseBone, 10 | ) 11 | from bpy_extras.io_utils import ( 12 | ImportHelper, 13 | orientation_helper, 14 | axis_conversion, 15 | ) 16 | 17 | bl_info = { 18 | "name": "Import Xom 3D Model / Animation", 19 | "author": "Psycrow", 20 | "version": (1, 2, 2), 21 | "blender": (2, 81, 0), 22 | "location": "File > Import-Export", 23 | "description": "Import Xom 3D Model / Animation from XomView format (.xom3d, .xac)", 24 | "warning": "", 25 | "wiki_url": "", 26 | "support": 'COMMUNITY', 27 | "category": "Import-Export" 28 | } 29 | 30 | if "bpy" in locals(): 31 | import importlib 32 | 33 | if "import_xom3d" in locals(): 34 | importlib.reload(import_xom3d) 35 | 36 | 37 | @orientation_helper(axis_forward='-Z', axis_up='Y') 38 | class ImportXom3D(bpy.types.Operator, ImportHelper): 39 | bl_idname = "import_scene.xom3d" 40 | bl_label = "Import Xom 3D Model / Animation" 41 | bl_options = {'PRESET', 'UNDO'} 42 | 43 | filename_ext = ".xom3d" 44 | filter_glob: StringProperty(default="*.xom3d;*.xac", options={'HIDDEN'}) 45 | 46 | use_def_pose: BoolProperty( 47 | name="Reset pose", 48 | description="Reset pose to default before loading animation", 49 | default=True, 50 | ) 51 | 52 | use_auto_smooth: BoolProperty( 53 | name="Use auto smooth", 54 | description="Use custom normals from mesh with auto smoothing", 55 | default=True, 56 | ) 57 | 58 | remove_doubles: BoolProperty( 59 | name="Remove doubles vertices", 60 | description="Remove doubles vertices", 61 | default=True, 62 | ) 63 | 64 | def execute(self, context): 65 | from . import import_xom3d 66 | 67 | keywords = self.as_keywords(ignore=("axis_forward", 68 | "axis_up", 69 | "filter_glob", 70 | )) 71 | keywords["global_matrix"] = axis_conversion(from_forward=self.axis_forward, 72 | from_up=self.axis_up, 73 | ).to_4x4() 74 | 75 | return import_xom3d.load(context, **keywords) 76 | 77 | 78 | def menu_func_import(self, context): 79 | self.layout.operator(ImportXom3D.bl_idname, 80 | text="Xom 3DModel (.xom3d, .xac)") 81 | 82 | 83 | class XomChildItem(bpy.types.PropertyGroup): 84 | child_name: StringProperty(name="XOM Child Name", options={'HIDDEN'}) 85 | 86 | 87 | classes = ( 88 | ImportXom3D, 89 | XomChildItem, 90 | ) 91 | 92 | 93 | def register(): 94 | for cls in classes: 95 | bpy.utils.register_class(cls) 96 | 97 | bpy.types.TOPBAR_MT_file_import.append(menu_func_import) 98 | 99 | PoseBone.xom_type = StringProperty(name="XOM Type", options={'HIDDEN'}) 100 | PoseBone.xom_location = FloatVectorProperty(name="XOM Location", options={'HIDDEN'}) 101 | PoseBone.xom_rotation = FloatVectorProperty(name="XOM Rotation", options={'HIDDEN'}) 102 | PoseBone.xom_scale = FloatVectorProperty(name="XOM Scale", options={'HIDDEN'}) 103 | PoseBone.xom_jointorient = FloatVectorProperty(name="XOM Joint Orientation", options={'HIDDEN'}) 104 | 105 | PoseBone.xom_child_selector = CollectionProperty(type=XomChildItem, options={'HIDDEN'}) 106 | 107 | PoseBone.xom_has_base = BoolProperty(name="XOM Has Base", options={'HIDDEN'}) 108 | PoseBone.xom_base_location = FloatVectorProperty(name="XOM Base Location", options={'HIDDEN'}) 109 | PoseBone.xom_base_rotation = FloatVectorProperty(name="XOM Base Rotation", options={'HIDDEN'}) 110 | PoseBone.xom_base_scale = FloatVectorProperty(name="XOM Base Scale", options={'HIDDEN'}) 111 | PoseBone.xom_base_cs = bpy.props.FloatProperty(name="XOM Base Child Selector", options={'HIDDEN'}) 112 | 113 | bpy.types.Object.xom_base_tex = bpy.props.FloatVectorProperty(name="XOM Base Texture Offset", options={'HIDDEN'}) 114 | 115 | 116 | def unregister(): 117 | bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) 118 | 119 | for cls in classes: 120 | bpy.utils.unregister_class(cls) 121 | 122 | 123 | if __name__ == "__main__": 124 | register() 125 | -------------------------------------------------------------------------------- /io_scene_xom3d/import_xom3d.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import bmesh 3 | import os 4 | import struct 5 | import math 6 | import mathutils 7 | from bpy_extras.image_utils import load_image 8 | from bpy_extras import node_shader_utils 9 | 10 | 11 | def trans_matrix(v): 12 | return mathutils.Matrix.Translation(v) 13 | 14 | 15 | def scale_matrix(v): 16 | mat = mathutils.Matrix.Identity(4) 17 | mat[0][0], mat[1][1], mat[2][2] = v[0], v[1], v[2] 18 | return mat 19 | 20 | 21 | def rotate_matrix(v): 22 | rot = mathutils.Euler(v, 'XYZ').to_quaternion() 23 | return rot.to_matrix().to_4x4() 24 | 25 | 26 | def find_bezier(x, a1, b1, c1, d1, a2, b2, c2, d2): 27 | def bezier_f(t, a, b, c, d): 28 | t1 = 1 - t 29 | return (t1 ** 3 * a) + (3 * t * t1 ** 2 * b) + (3 * t ** 2 * t1 * c) + (t ** 3 * d) 30 | 31 | def find(t1, t2, t3): 32 | xdiv = bezier_f(t2, a1, b1, c1, d1) 33 | if abs(xdiv - x) < 0.001: 34 | return t2 35 | 36 | if xdiv > x: 37 | return find(t1, (t1 + t2) / 2, t2) 38 | 39 | return find(t2, (t2 + t3) / 2, t3) 40 | 41 | return bezier_f(find(0, 0.5, 1.0), a2, b2, c2, d2) 42 | 43 | 44 | def calculate_frame_value(k, frames_data): 45 | left_frame, right_frame, cur_frame = None, None, None 46 | 47 | for f in frames_data: 48 | if f[0] < k: 49 | left_frame = f 50 | elif f[0] == k: 51 | cur_frame = f 52 | break 53 | else: 54 | right_frame = f 55 | break 56 | 57 | if cur_frame: 58 | return cur_frame[1] 59 | 60 | if left_frame and right_frame: 61 | a = left_frame[0] + (right_frame[0] - left_frame[0]) * math.cos(left_frame[5]) * left_frame[4] / 3 62 | b = left_frame[1] + (right_frame[1] - left_frame[1]) * math.sin(left_frame[5]) * left_frame[4] / 3 63 | c = right_frame[0] - (right_frame[0] - left_frame[0]) * math.cos(right_frame[3]) * right_frame[2] / 3 64 | d = right_frame[1] - (right_frame[1] - left_frame[1]) * math.sin(right_frame[3]) * right_frame[2] / 3 65 | 66 | return find_bezier(k, left_frame[0], a, c, right_frame[0], left_frame[1], b, d, right_frame[1]) 67 | 68 | if left_frame: 69 | return left_frame[1] 70 | 71 | return right_frame[1] 72 | 73 | 74 | def read_byte(fd): 75 | return struct.unpack(' 0: 437 | vg.add([v], w[i], 'REPLACE') 438 | 439 | if use_auto_smooth: 440 | mesh_object.data.use_auto_smooth = True 441 | 442 | if remove_doubles: 443 | view_layer.objects.active = mesh_object 444 | bpy.ops.object.mode_set(mode='EDIT') 445 | bpy.ops.mesh.remove_doubles() 446 | bpy.ops.object.mode_set(mode='OBJECT') 447 | 448 | # append mesh name to XChildSelector 449 | if node["parent"]["type"] == 'CS': 450 | parent_bone.xom_child_selector.add().child_name = mesh_object.name 451 | if len(parent_bone.xom_child_selector) > 1: 452 | mesh_object.hide_viewport = True 453 | mesh_object.hide_render = True 454 | 455 | # set up pose bones 456 | view_layer.objects.active = armature_object 457 | bpy.ops.object.mode_set(mode='POSE') 458 | for b in armature_object.pose.bones: 459 | b.rotation_mode = 'XYZ' 460 | 461 | for node in nodes_data: 462 | if node['name'] == b.name: 463 | b.xom_type = node['type'] 464 | b.xom_location = node['position'] 465 | b.xom_rotation = node['rotation'] 466 | b.xom_scale = node['scale'] 467 | 468 | if b.xom_type == 'BG': 469 | b.rotation_euler = node["jointorient"] 470 | b.xom_jointorient = b.rotation_euler.copy() 471 | 472 | break 473 | 474 | bpy.ops.object.mode_set(mode='OBJECT') 475 | armature_object.matrix_world = global_matrix 476 | 477 | for o in view_layer.objects: 478 | o.select_set(o == armature_object) 479 | 480 | view_layer.update() 481 | return {'FINISHED'} 482 | 483 | 484 | def load_xom3d_animation(filepath, context, use_def_pose): 485 | view_layer = context.view_layer 486 | 487 | armature_object = view_layer.objects.active 488 | 489 | if not armature_object: 490 | return {'CANCELLED'} 491 | 492 | armature = armature_object.data 493 | if type(armature) != bpy.types.Armature: 494 | return {'CANCELLED'} 495 | 496 | bpy.ops.object.mode_set(mode='POSE') 497 | 498 | # reset pose 499 | if use_def_pose: 500 | armature_object.animation_data_clear() 501 | for b in armature_object.pose.bones: 502 | b.rotation_euler = mathutils.Euler(b.xom_jointorient) 503 | b.location.zero() 504 | b.scale = mathutils.Vector((1, 1, 1)) 505 | 506 | # reset Child Selector 507 | if b.xom_child_selector: 508 | for i, c in enumerate(b.xom_child_selector): 509 | mesh_object = view_layer.objects.get(c.child_name) 510 | if mesh_object: 511 | mesh_object.animation_data_clear() 512 | mesh_object.hide_viewport = (i != 0) 513 | mesh_object.hide_render = (i != 0) 514 | 515 | # reset texture offsets 516 | for obj in view_layer.objects: 517 | if obj.parent == armature_object: 518 | node_tree = obj.active_material.node_tree 519 | node_tree.animation_data_clear() 520 | mapping_node = node_tree.nodes.get('Mapping') 521 | if mapping_node: 522 | mapping_node.inputs['Location'].default_value = mathutils.Vector((0.0, 0.0, 0.0)) 523 | 524 | file = open(filepath, 'rb') 525 | 526 | anim_name = read_string(file) 527 | maxkey = read_float(file) 528 | num = read_short(file) 529 | fps = context.scene.render.fps 530 | last_frame = fps * maxkey 531 | 532 | loc_data, rot_data, scale_data, tex_data, child_data = {}, {}, {}, {}, {} 533 | 534 | for _ in range(num): 535 | obj_name = read_string(file) 536 | prs_type = read_short(file) 537 | xyz_type = read_short(file) 538 | keys = read_short(file) 539 | 540 | bone = armature_object.pose.bones.get(obj_name) 541 | 542 | if xyz_type == 0: 543 | axis = 0 544 | elif xyz_type == 256: 545 | axis = 1 546 | else: 547 | axis = 2 548 | 549 | animation_data = [] 550 | 551 | for _ in range(keys): 552 | c1 = read_float(file) 553 | c2 = read_float(file) 554 | c3 = read_float(file) 555 | c4 = read_float(file) 556 | 557 | frame = read_float(file) * fps 558 | value = read_float(file) 559 | 560 | # fix the scale of the worm animation 561 | if bone and bone.xom_has_base and prs_type == 2308: 562 | value *= 0.5 563 | 564 | animation_data.append([frame, value, c1, c2, c3, c4]) 565 | 566 | if prs_type == 1025: 567 | if view_layer.objects.get(obj_name): 568 | if not tex_data.get(obj_name): 569 | tex_data[obj_name] = [None, None] 570 | tex_data[obj_name][axis] = animation_data 571 | # check the correct bone 572 | elif bone and bone.xom_type: 573 | if prs_type == 258: 574 | if not loc_data.get(obj_name): 575 | loc_data[obj_name] = [None, None, None] 576 | loc_data[obj_name][axis] = animation_data 577 | elif prs_type == 259: 578 | if not rot_data.get(obj_name): 579 | rot_data[obj_name] = [None, None, None] 580 | rot_data[obj_name][axis] = animation_data 581 | elif prs_type == 2308: 582 | if not scale_data.get(obj_name): 583 | scale_data[obj_name] = [None, None, None] 584 | scale_data[obj_name][axis] = animation_data 585 | elif prs_type == 4352: 586 | child_data[obj_name] = animation_data 587 | 588 | file.close() 589 | 590 | context.scene.frame_start = 0 591 | context.scene.frame_end = int(last_frame) 592 | 593 | action = bpy.data.actions.new(anim_name) 594 | 595 | # scale 596 | for bone_name, data in scale_data.items(): 597 | bone = armature_object.pose.bones[bone_name] 598 | 599 | if bone.xom_type == 'BG': 600 | scale_origin_data = mathutils.Vector((1, 1, 1)) 601 | else: 602 | scale_origin_data = bone.xom_scale 603 | 604 | bone_string = 'pose.bones["{}"].'.format(bone_name) 605 | 606 | if bone_name in action.groups.keys(): 607 | group = action.groups[bone_name] 608 | else: 609 | group = action.groups.new(name=bone_name) 610 | 611 | for axis in (0, 1, 2): 612 | frames_data = data[axis] 613 | 614 | if not frames_data: 615 | continue 616 | 617 | curve = action.fcurves.new(data_path=bone_string + "scale", index=axis) 618 | curve.group = group 619 | 620 | frames_num = math.ceil((max(frames_data, key=lambda _f: _f[0])[0])) + 1 621 | for k in range(frames_num): 622 | val = 1.0 + bone.xom_base_scale[axis] + calculate_frame_value(k, frames_data) - scale_origin_data[axis] 623 | curve.keyframe_points.add(1) 624 | curve.keyframe_points[k].co = k, val 625 | curve.keyframe_points[k].interpolation = 'LINEAR' 626 | 627 | # rotation 628 | for bone_name, data in rot_data.items(): 629 | bone = armature_object.pose.bones[bone_name] 630 | 631 | if bone.xom_type == 'BG': 632 | rot_origin_data = mathutils.Euler((0, 0, 0)) 633 | else: 634 | rot_origin_data = bone.xom_rotation 635 | 636 | bone_string = 'pose.bones["{}"].'.format(bone_name) 637 | 638 | if bone_name in action.groups.keys(): 639 | group = action.groups[bone_name] 640 | else: 641 | group = action.groups.new(name=bone_name) 642 | 643 | for axis in (0, 1, 2): 644 | frames_data = data[axis] 645 | 646 | if not frames_data: 647 | continue 648 | 649 | curve = action.fcurves.new(data_path=bone_string + "rotation_euler", index=axis) 650 | curve.group = group 651 | 652 | frames_num = math.ceil((max(frames_data, key=lambda _f: _f[0])[0])) + 1 653 | for k in range(frames_num): 654 | val = bone.xom_base_rotation[axis] + calculate_frame_value(k, frames_data) - rot_origin_data[axis] 655 | curve.keyframe_points.add(1) 656 | curve.keyframe_points[k].co = k, val 657 | curve.keyframe_points[k].interpolation = 'LINEAR' 658 | 659 | # fix prevent zero vectors 660 | temp_scale = {} 661 | for b in armature_object.pose.bones: 662 | temp_scale[b], b.scale = b.scale.copy(), mathutils.Vector((1, 1, 1)) 663 | view_layer.update() 664 | 665 | # location 666 | for bone_name, data in loc_data.items(): 667 | bone = armature_object.pose.bones[bone_name] 668 | 669 | loc_origin_data = mathutils.Vector(bone.xom_location) 670 | is_bone = bone.xom_type == 'BG' 671 | 672 | bone_string = 'pose.bones["{}"].'.format(bone_name) 673 | 674 | if bone_name in action.groups.keys(): 675 | group = action.groups[bone_name] 676 | else: 677 | group = action.groups.new(name=bone_name) 678 | 679 | frames_num = 0 680 | for frames in data: 681 | if frames: 682 | max_num = math.ceil(max(frames, key=lambda _f: _f[0])[0]) 683 | if frames_num < max_num: 684 | frames_num = max_num 685 | frames_num += 1 686 | 687 | loc_apply_data = [loc_origin_data.copy() for _ in range(frames_num)] 688 | 689 | curves = [action.fcurves.new(data_path=bone_string + "location", index=axis) for axis in (0, 1, 2)] 690 | 691 | for axis in (0, 1, 2): 692 | frames_data = data[axis] 693 | curves[axis].group = group 694 | 695 | if not frames_data: 696 | continue 697 | 698 | for k in range(frames_num): 699 | loc_apply_data[k][axis] = bone.xom_base_location[axis] + calculate_frame_value(k, frames_data) 700 | 701 | if bone.parent: 702 | p_x_ax = bone.parent.x_axis 703 | p_y_ax = bone.parent.y_axis 704 | p_z_ax = bone.parent.z_axis 705 | else: 706 | p_x_ax = (1, 0, 0) 707 | p_y_ax = (0, 1, 0) 708 | p_z_ax = (0, 0, 1) 709 | 710 | x_ax = bone.x_axis 711 | y_ax = bone.y_axis 712 | z_ax = bone.z_axis 713 | 714 | for k in range(frames_num): 715 | vector1 = loc_origin_data.copy() 716 | vector2 = loc_apply_data[k].copy() 717 | 718 | if bone.parent: 719 | vec1 = vector1.copy() 720 | vec2 = vector2.copy() 721 | 722 | for i in (0, 1, 2): 723 | vector1[i] = vec1[0] * p_x_ax[i] + vec1[1] * p_y_ax[i] + vec1[2] * p_z_ax[i] 724 | vector2[i] = vec2[0] * p_x_ax[i] + vec2[1] * p_y_ax[i] + vec2[2] * p_z_ax[i] 725 | 726 | dif_vec = vector2 - vector1 727 | 728 | if is_bone: 729 | vec = dif_vec.copy() 730 | dif_vec[0] = vec[0] * x_ax[0] + vec[1] * x_ax[1] + vec[2] * x_ax[2] 731 | dif_vec[1] = vec[0] * y_ax[0] + vec[1] * y_ax[1] + vec[2] * y_ax[2] 732 | dif_vec[2] = vec[0] * z_ax[0] + vec[1] * z_ax[1] + vec[2] * z_ax[2] 733 | else: 734 | d = x_ax[0] * y_ax[1] * z_ax[2] + x_ax[1] * y_ax[2] * z_ax[0] + x_ax[2] * y_ax[0] * z_ax[1] - x_ax[2] * \ 735 | y_ax[1] * z_ax[0] - x_ax[0] * y_ax[2] * z_ax[1] - x_ax[1] * y_ax[0] * z_ax[2] 736 | d1 = dif_vec[0] * y_ax[1] * z_ax[2] + x_ax[1] * y_ax[2] * dif_vec[2] + x_ax[2] * dif_vec[1] * z_ax[1] - \ 737 | x_ax[2] * y_ax[1] * dif_vec[2] - dif_vec[0] * y_ax[2] * z_ax[1] - x_ax[1] * dif_vec[1] * z_ax[2] 738 | d2 = x_ax[0] * dif_vec[1] * z_ax[2] + dif_vec[0] * y_ax[2] * z_ax[0] + x_ax[2] * y_ax[0] * dif_vec[2] - \ 739 | x_ax[2] * dif_vec[1] * z_ax[0] - x_ax[0] * y_ax[2] * dif_vec[2] - dif_vec[0] * y_ax[0] * z_ax[2] 740 | d3 = x_ax[0] * y_ax[1] * dif_vec[2] + x_ax[1] * dif_vec[1] * z_ax[0] + dif_vec[0] * y_ax[0] * z_ax[1] - \ 741 | dif_vec[0] * y_ax[1] * z_ax[0] - x_ax[0] * dif_vec[1] * z_ax[1] - x_ax[1] * y_ax[0] * dif_vec[2] 742 | 743 | dif_vec[0] = d1 / d 744 | dif_vec[1] = d2 / d 745 | dif_vec[2] = d3 / d 746 | 747 | for axis in (0, 1, 2): 748 | curves[axis].keyframe_points.add(1) 749 | curves[axis].keyframe_points[k].co = k, dif_vec[axis] 750 | curves[axis].keyframe_points[k].interpolation = 'LINEAR' 751 | 752 | for b in armature_object.pose.bones: 753 | b.scale = temp_scale[b] 754 | 755 | # texture animation 756 | for obj_name, data in tex_data.items(): 757 | obj = view_layer.objects[obj_name] 758 | node_tree = obj.active_material.node_tree 759 | 760 | obj_action = bpy.data.actions.new(node_tree.name) 761 | group = action.groups.new(name=node_tree.name) 762 | 763 | for axis in (0, 1): 764 | frames_data = data[axis] 765 | 766 | if not frames_data: 767 | continue 768 | 769 | curve = obj_action.fcurves.new(data_path='nodes["Mapping"].inputs["Location"].default_value', index=axis) 770 | curve.group = group 771 | 772 | frames_num = math.ceil((max(frames_data, key=lambda _f: _f[0])[0])) + 1 773 | for k in range(frames_num): 774 | val = obj.xom_base_tex[axis] + calculate_frame_value(k, frames_data) 775 | curve.keyframe_points.add(1) 776 | curve.keyframe_points[k].co = k, -val 777 | curve.keyframe_points[k].interpolation = 'LINEAR' 778 | 779 | node_tree.animation_data_create().action = obj_action 780 | 781 | # child selector animation (CS) 782 | for bone_name, data in child_data.items(): 783 | bone = armature_object.pose.bones[bone_name] 784 | 785 | for i, c in enumerate(bone.xom_child_selector): 786 | mesh_object = view_layer.objects.get(c.child_name) 787 | if not mesh_object: 788 | continue 789 | 790 | mesh_action = bpy.data.actions.new(mesh_object.name) 791 | group = mesh_action.groups.new(name=mesh_object.name) 792 | 793 | curve1 = mesh_action.fcurves.new(data_path='hide_viewport') 794 | curve1.group = group 795 | 796 | curve2 = mesh_action.fcurves.new(data_path='hide_render') 797 | curve2.group = group 798 | 799 | for d in data: 800 | val = math.ceil(bone.xom_base_cs + d[1]) - 1 801 | 802 | curve1.keyframe_points.add(1) 803 | curve2.keyframe_points.add(1) 804 | 805 | curve1.keyframe_points[-1].co = d[0], val != i 806 | curve2.keyframe_points[-1].co = d[0], val != i 807 | 808 | curve1.keyframe_points[-1].interpolation = 'CONSTANT' 809 | curve2.keyframe_points[-1].interpolation = 'CONSTANT' 810 | 811 | mesh_object.animation_data_create().action = mesh_action 812 | 813 | # save base data 814 | if anim_name == 'Base': 815 | for bone_name, data in scale_data.items(): 816 | bone = armature_object.pose.bones[bone_name] 817 | bone.xom_has_base = True 818 | 819 | vec = mathutils.Vector((0, 0, 0)) 820 | for axis in (0, 1, 2): 821 | if data[axis]: 822 | vec[axis] = data[axis][0][1] * 0.5 823 | bone.xom_base_scale = vec 824 | 825 | for bone_name, data in rot_data.items(): 826 | bone = armature_object.pose.bones[bone_name] 827 | bone.xom_has_base = True 828 | 829 | vec = mathutils.Vector((0, 0, 0)) 830 | for axis in (0, 1, 2): 831 | if data[axis]: 832 | vec[axis] = data[axis][0][1] 833 | bone.xom_base_rotation = vec 834 | 835 | for bone_name, data in loc_data.items(): 836 | bone = armature_object.pose.bones[bone_name] 837 | bone.xom_has_base = True 838 | 839 | vec = mathutils.Vector((0, 0, 0)) 840 | for axis in (0, 1, 2): 841 | if data[axis]: 842 | vec[axis] = data[axis][0][1] 843 | bone.xom_base_location = vec 844 | 845 | for obj_name, data in tex_data.items(): 846 | obj = view_layer.objects[obj_name] 847 | vec = mathutils.Vector((0, 0, 0)) 848 | for axis in (0, 1): 849 | if data[axis]: 850 | vec[axis] = data[axis][0][1] 851 | obj.xom_base_tex = vec 852 | 853 | for bone_name, data in child_data.items(): 854 | bone = armature_object.pose.bones[bone_name] 855 | bone.xom_has_base = True 856 | bone.xom_base_cs = data[0][1] 857 | 858 | armature_object.animation_data_create().action = action 859 | 860 | bpy.ops.object.mode_set(mode='OBJECT') 861 | return {'FINISHED'} 862 | 863 | 864 | def load(context, filepath, *, use_def_pose, use_auto_smooth, remove_doubles, global_matrix=None): 865 | filepath_lc = filepath.lower() 866 | if filepath_lc.endswith('.xom3d'): 867 | return load_xom3d_mesh(filepath, context, use_auto_smooth, remove_doubles, global_matrix) 868 | elif filepath_lc.endswith('.xac'): 869 | return load_xom3d_animation(filepath, context, use_def_pose) 870 | 871 | return {'CANCELLED'} 872 | --------------------------------------------------------------------------------