Saving/loading the location(x,y,z) of selected vertices

I have multiple object in blender. There is an object ,lets called it “Obj1” ,which is the object I have to edit.
The model compile process does not allow to change the indices and vert-count (vertex 's ordinal number) of that object “Obj1” during modification,
so I won’t add/delete vertices ,faces ,edges to it during my modification.
All others transform (rotate ,scale ,move) are fine (but of course we can freely do anything with others object).
All faces are triangle.

Here is what I am asking for :
(1)
I want to save those information of “Obj1” into a file (for restore in the future which I describe in the part (2) below):
-The location(x,y,z) of all vertices,and their ordinal numbers (vertex orders)
-The information of all the edges (the edge connect which vertices with which vertices)
-The information of all face (the face was created by which vertices)
-The normal vector is not saved

(2).
After modify the object ,if I want to restore the selected vertices (only restore the selected vertices).
The script will allow me to choose the file to import ,and after importing,it would :
-Restore the selected vertices into position that saved in file
-And also restore the edges/faces that connect to them
-Indices and vert-count are unchanged

zeffii has wrote a script for me ,it works fine.
However if I rotated the edge (by using Mesh => Faces => Rotate Edges) ,
When I restore , this script can’t restore the edge to original.

I really appreciate if somebody can help me to complete it.
Here is the script :

bl_info = {
    "name": "CloneRestore",
    "author": "Dealga McArdle",
    "version": (0, 2),
    "blender": (2, 7, 6),
    "location": "Object -> Tools -> Misc",
    "description": "clones and restores from clone",
    "warning": "20 Nov 2015",
    "wiki_url": "",
    "tracker_url": "",
    "category": "3D View"
}

import os
import json

import bpy
from bpy_extras.io_utils import ExportHelper


mode_set = bpy.ops.object.mode_set


def io_import():
    ctx = bpy.context
    ob, directory = ctx.active_object, ctx.scene.clone_io_directory

    full_path = os.path.join(directory, ob.name + '.json')
    with open(full_path) as ofile:
        text_str = ''.join(ofile.readlines())
        my_json = json.loads(text_str)
        verts, faces = my_json['verts'], my_json['faces']
        mesh = bpy.data.meshes.new(ob.name + "_clone")
        mesh.from_pydata(verts, [], faces)
        mesh.use_fake_user = True
        mesh.update()


def io_export():
    ctx = bpy.context
    ob, directory = ctx.active_object, ctx.scene.clone_io_directory

    full_path = os.path.join(directory, ob.name + '.json')
    with open(full_path, 'w') as ofile:

        verts = ob.data.vertices
        polygons = ob.data.polygons
        my_dict = {
            'verts': [v.co[:] for v in verts],
            'faces': [f.vertices[:] for f in polygons]
        }

        m = json.dumps(my_dict, sort_keys=True)
        ofile.write(m)


def clone():
    ob = bpy.context.active_object

    mode_set(mode='OBJECT')
    clone_mesh = ob.data.copy()
    clone_mesh.name = ob.name + '_clone'
    clone_mesh.use_fake_user = True

    mode_set(mode='EDIT')


def restore():
    ob = bpy.context.active_object
    mode_set(mode='OBJECT')

    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if v.select:
            v.co = clone_verts[v.index].co

    mode_set(mode='EDIT')


def changed():
    ob = bpy.context.active_object
    bpy.ops.mesh.select_all(action='DESELECT')

    mode_set(mode='OBJECT')
    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if (v.co - clone_verts[v.index].co).length > 0.0001:
            v.select = True

    mode_set(mode='EDIT')


class ClonedRestoreOpsDirectorySelector(bpy.types.Operator, ExportHelper):
    bl_idname = "object.io_folder_selector"
    bl_label = "json folder"

    filename_ext = "."
    use_filter_folder = True

    def execute(self, context):
        # even if you pick a file i'll strip it and get the dirname
        fdir = self.properties.filepath
        dp = os.path.dirname(fdir)
        context.scene.clone_io_directory = dp
        return{'FINISHED'}


class ClonedRestoreOps(bpy.types.Operator):

    bl_idname = "object.cloned_restore_op"
    bl_label = "Short Name"

    fn_name = bpy.props.StringProperty(default='')

    def execute(self, context):
        if self.fn_name in {'io_import', 'io_export'}:
            if not os.path.isdir(context.scene.clone_io_directory):
                return {'CANCELLED'}

        exec(self.fn_name + '()')
        return {'FINISHED'}


class ClonedRestore(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Clone Restore Panel"
    bl_idname = "OBJECT_PT_clone_restore"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        layout = self.layout

        obj = context.object
        col = layout.column()

        operators = 'clone', 'restore', 'changed'
        for op in operators:
            ob = context.active_object
            if op == 'clone' and ob and (ob.name + '_clone' in bpy.data.meshes):
                col.label('cloned as: ' + ob.name + '_clone')
            else:
                col.operator("object.cloned_restore_op", text=op).fn_name = op

        scn = context.scene
        col.label('I / O')

        row = col.row(align=True)
        row.prop(scn, 'clone_io_directory', text='directory:')
        row.operator("object.io_folder_selector", icon="FILE_FOLDER", text="")

        col.operator("object.cloned_restore_op", text='import').fn_name = 'io_import'
        col.operator("object.cloned_restore_op", text='export').fn_name = 'io_export'


def register():
    bpy.types.Scene.clone_io_directory = bpy.props.StringProperty()
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.clone_io_directory


if __name__ == "__main__":
    register()

Thanks

P/S : Here 's what zeffi describe about his script (if anybody interested) :

You can either pick a directory using the selector or paste it in manually. Even if you pick a file from the FileBrowser it will strip the path to just be a Folder/Directory.

  It appears in the Misc panel:

http://i.stack.imgur.com/rnigu.png

If you have the Suzanne Object selected it can clone the object and make a hidden mesh, or look in the directory provided for a Suzanne.json, which it can load as the clone mesh.

  • clone: Clones the Object’s mesh as a hidden mesh called <object_name>_clone
  • restore: Restores the locations of the vertices currently selected
  • changed: Selects only those vertices which changed their locations
  • import: Tries to import a hidden mesh. The currently selected object’s name is used to find the json: directory/<object_name>.json
  • export: Makes a <object_name>.json in the directory pointed at.

You can use this:


import bpy


obj = bpy.context.object
for v in obj.data.vertices:
    print("index=" + str(v.index) + " loc=" + str(v.co))

This gets the location of vertices in local space.

To write in a file, use normal Python IO routines.

Thank you very much antonioya , ireally appreciate your effort

I am still beginer in coding python (i have just started learning when encounter this problem) , so i greatly appreciate if somebody can help me to make it complete , while i still learn how to code it
Thanks alot

Do you need the restore functionality only because it can happen that you accidentally edit the mesh?
If so, wouldn’t it be easier to link the mesh from another .blend and attach it to a local object? That should prevent any modifications to the mesh, while still allowing loc/rot/scale operations on the object.

You could also use shape keys, to store the initial vertex locations.

Thank you CodemanX ,that 's not what i want , while editing model, there are manysteps , so I save each step to a blender file for example like this.
Step1 : save as file 1.blend : edit the leg for fit the trouser.
Step2 : save as file 2.blend : edit the arm of file 1 (continue of file1).
then I put into test in my game,if I feel not satisfied with the leg that I have edited in step1 (file1).
i can revert the leg back to the original one (before i edited in step1 (file1) but still keep the arm that I edited in step2
and i want to save it into file for easily manage saving /loading

zeffii has wrote the code that load /save location ,but it does not have “saving /loading” file feature yet ( i want to save the information of vertices into file ,when loading the file, its load and change the vertices position

  • locations of verts of the initial object are stored
  • indices and vert-count are unchanged
  • coordinates can change.
  • we are able to restore those verts in your modified object which are currently selected.

import bpy

mode_set = bpy.ops.object.mode_set


def clone():
    ob = bpy.context.active_object

    mode_set(mode='OBJECT')
    clone_mesh = ob.data.copy()
    clone_mesh.name = ob.name + '_clone'
    clone_mesh.use_fake_user = True

    mode_set(mode='EDIT')


def restore():
    ob = bpy.context.active_object
    mode_set(mode='OBJECT')

    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if v.select:
            v.co = clone_verts[v.index].co

    mode_set(mode='EDIT')


def changed():
    ob = bpy.context.active_object
    bpy.ops.mesh.select_all(action='DESELECT')

    mode_set(mode='OBJECT')
    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if (v.co - clone_verts[v.index].co).length &gt; 0.0001:
            v.select = True

    mode_set(mode='EDIT')


class ClonedRestoreOps(bpy.types.Operator):

    bl_idname = "object.cloned_restore_op"
    bl_label = "Short Name"

    fn_name = bpy.props.StringProperty(default='')

    def execute(self, context):
        exec(self.fn_name + '()')
        return {'FINISHED'}


class ClonedRestore(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Clone Restore Panel"
    bl_idname = "OBJECT_PT_clone_restore"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        layout = self.layout

        obj = context.object
        col = layout.column()

        operators = 'clone', 'restore', 'changed'
        for op in operators:
            ob = context.active_object
            if op == 'clone' and ob and (ob.name + '_clone' in bpy.data.meshes):
                col.label('cloned as: ' + ob.name + '_clone')
            else:
                col.operator("object.cloned_restore_op", text=op).fn_name = op


def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()

Hopefully someone can help me to write export /import save file feature
Thanks again

Thank you @CoDEmanX ,i knew about them,but that’s not what i want

Here is what i want in the script

  • locations of verts of the initial object are stored
  • indices and vert-count are unchanged
  • coordinates can change.
  • i want to be able to restore those verts in my modified object which are currently selected - by saving object info into file and load them when i need

zeffii has wrote a script for me ,it works fine
However if i rotated the edge (by using Mesh => Faces => Rotate Edges) ,
When i restore , this script can’t restore the edge to original
i really appreciate if somebody can help me to complete it
Thanks so much
Here is the script :


bl_info = {
    "name": "CloneRestore",
    "author": "Dealga McArdle",
    "version": (0, 2),
    "blender": (2, 7, 6),
    "location": "Object -&gt; Tools -&gt; Misc",
    "description": "clones and restores from clone",
    "warning": "20 Nov 2015",
    "wiki_url": "",
    "tracker_url": "",
    "category": "3D View"
}

import os
import json

import bpy
from bpy_extras.io_utils import ExportHelper


mode_set = bpy.ops.object.mode_set


def io_import():
    ctx = bpy.context
    ob, directory = ctx.active_object, ctx.scene.clone_io_directory

    full_path = os.path.join(directory, ob.name + '.json')
    with open(full_path) as ofile:
        text_str = ''.join(ofile.readlines())
        my_json = json.loads(text_str)
        verts, faces = my_json['verts'], my_json['faces']
        mesh = bpy.data.meshes.new(ob.name + "_clone")
        mesh.from_pydata(verts, [], faces)
        mesh.use_fake_user = True
        mesh.update()


def io_export():
    ctx = bpy.context
    ob, directory = ctx.active_object, ctx.scene.clone_io_directory

    full_path = os.path.join(directory, ob.name + '.json')
    with open(full_path, 'w') as ofile:

        verts = ob.data.vertices
        polygons = ob.data.polygons
        my_dict = {
            'verts': [v.co[:] for v in verts],
            'faces': [f.vertices[:] for f in polygons]
        }

        m = json.dumps(my_dict, sort_keys=True)
        ofile.write(m)


def clone():
    ob = bpy.context.active_object

    mode_set(mode='OBJECT')
    clone_mesh = ob.data.copy()
    clone_mesh.name = ob.name + '_clone'
    clone_mesh.use_fake_user = True

    mode_set(mode='EDIT')


def restore():
    ob = bpy.context.active_object
    mode_set(mode='OBJECT')

    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if v.select:
            v.co = clone_verts[v.index].co

    mode_set(mode='EDIT')


def changed():
    ob = bpy.context.active_object
    bpy.ops.mesh.select_all(action='DESELECT')

    mode_set(mode='OBJECT')
    clone_mesh = bpy.data.meshes.get(ob.name + '_clone')
    clone_verts = clone_mesh.vertices
    for v in ob.data.vertices:
        if (v.co - clone_verts[v.index].co).length &gt; 0.0001:
            v.select = True

    mode_set(mode='EDIT')


class ClonedRestoreOpsDirectorySelector(bpy.types.Operator, ExportHelper):
    bl_idname = "object.io_folder_selector"
    bl_label = "json folder"

    filename_ext = "."
    use_filter_folder = True

    def execute(self, context):
        # even if you pick a file i'll strip it and get the dirname
        fdir = self.properties.filepath
        dp = os.path.dirname(fdir)
        context.scene.clone_io_directory = dp
        return{'FINISHED'}


class ClonedRestoreOps(bpy.types.Operator):

    bl_idname = "object.cloned_restore_op"
    bl_label = "Short Name"

    fn_name = bpy.props.StringProperty(default='')

    def execute(self, context):
        if self.fn_name in {'io_import', 'io_export'}:
            if not os.path.isdir(context.scene.clone_io_directory):
                return {'CANCELLED'}

        exec(self.fn_name + '()')
        return {'FINISHED'}


class ClonedRestore(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Clone Restore Panel"
    bl_idname = "OBJECT_PT_clone_restore"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        layout = self.layout

        obj = context.object
        col = layout.column()

        operators = 'clone', 'restore', 'changed'
        for op in operators:
            ob = context.active_object
            if op == 'clone' and ob and (ob.name + '_clone' in bpy.data.meshes):
                col.label('cloned as: ' + ob.name + '_clone')
            else:
                col.operator("object.cloned_restore_op", text=op).fn_name = op

        scn = context.scene
        col.label('I / O')

        row = col.row(align=True)
        row.prop(scn, 'clone_io_directory', text='directory:')
        row.operator("object.io_folder_selector", icon="FILE_FOLDER", text="")

        col.operator("object.cloned_restore_op", text='import').fn_name = 'io_import'
        col.operator("object.cloned_restore_op", text='export').fn_name = 'io_export'


def register():
    bpy.types.Scene.clone_io_directory = bpy.props.StringProperty()
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.clone_io_directory


if __name__ == "__main__":
    register()

Any help would be much appreciated

P/S : Sorry my post appears late because it needs to approved by forum moderator