1

I am using only objects from photogrammetry and the scale is far from ok when I import them. I would like to be able to scale it automatically on selection if its mesh volume is lower than a precise value.

I tried to do this like below :

def setup_handler():
   for h in [x for x in bpy.app.handlers.scene_update_pre]:
       if h == scale_function:
           bpy.app.handlers.scene_update_pre.remove( scale_function )

   bpy.app.handlers.scene_update_pre.append( scale_function )

def scale_function():
    if bpy.types.Object.test != scn.objects.active.name :
        bpy.types.Object.test = scn.objects.active.name
        if bpy.context.mode == 'OBJECT':
            volume = volumeDeterminationFunction()
            if volume <= 1 :
                scale=3
                bpy.ops.transform.resize(value=(scale,) * 3, mirror=False, proportional='DISABLED', snap=False, texture_space=False)

I call setup_handler function in a draw function class, but it doesn't work, maybe I should call the function elsewhere ? if you have any idea ? Thanks

art2611
  • 97
  • 1
  • 6
  • You can use handlers to fire your function. See https://docs.blender.org/api/2.79/bpy.app.handlers.html (not the same in 2.8 afaik). Usage here in this answer (scene handler part): https://blender.stackexchange.com/questions/143273/how-to-create-a-tetrix-sierpinski-tetrahedron-fractal-radiating-from-0-0-0-pyt/144493#144493 – lemon Jul 23 '19 at 10:48
  • What importer? does it have a scale property. What is possibly easiest here is to add your own operator that imports and post processes. – batFINGER Jul 23 '19 at 13:02
  • I mean, when I import an Obj object, I would like to scale it on the same time if it's too small, but I am looking for solutions. I really don't know how to create an operator to do those two actions but it would be perfect, I am a novice in blender's code.. If you could show me an example or something I would be grateful ! – art2611 Jul 23 '19 at 13:11
  • Do it in the register function of your addon. See https://blender.stackexchange.com/questions/9357/how-to-manage-handlers-in-an-add-on – lemon Jul 23 '19 at 14:16
  • I just tried this line in the register and unregister function : bpy.app.handlers.scene_update_pre.append(setup_handler) Still not working.. I don't understand what I am doing wrong – art2611 Jul 23 '19 at 14:39

1 Answers1

1

Use the import operator in another.

As commented, rather than importing, then waiting for object selection to rescale, recommend rescaling on import.

Here is script that uses the template Text Editor > Templates > Python > Operator Import as a starting point.

Note I am using 2.8. If you are using 2.7x or before, adjust to reflect that version accordingly. Most notably annotations. where the annotation foo : BarProperty() needs to be replaced with assignment foo = BarProperty()

Assumes you are doing little else than choosing a filename of obj, and when selected passes it to the import operator.

After import the new objects are selected, and in the context.selected_objects list.

Following test script prints out name and a volume based on object dimensions.

import bpy


def read_some_data(context, filepath):

    bpy.ops.import_scene.obj(filepath=filepath)
    for o in context.selected_objects:

        dim = o.dimensions
        vol = dim.x * dim.y * dim.z
        print(o.name, vol)
        # scale based on volume
        if vol < 1:
            o.scale *= 2

    return {'FINISHED'}


from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator


class ImportSomeData(Operator, ImportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "import_test.some_data" 
    bl_label = "Import Some Data"

    filename_ext = ".obj"

    filter_glob: StringProperty(
        default="*.obj",
        options={'HIDDEN'},
        maxlen=255,  # Max internal buffer length, longer would be clamped.
    )

    def execute(self, context):
        return read_some_data(context, self.filepath)


# Only needed if you want to add into a dynamic menu
def menu_func_import(self, context):
    self.layout.operator(ImportSomeData.bl_idname, text="Text Import Operator")


def register():
    bpy.utils.register_class(ImportSomeData)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)


def unregister():
    bpy.utils.unregister_class(ImportSomeData)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.import_test.some_data('INVOKE_DEFAULT')
batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • It really the perfect solution here, thank you a lot for the time you took to answer me. – art2611 Jul 23 '19 at 15:00
  • However, just a little precision if possible please, how I save the operator Import when the entire code is replaced by yours ? Run script is not working :/ – art2611 Jul 23 '19 at 15:45
  • Example written for 2.8. If you are using version 2.7x or prior will need to amend script accordingly. See edit. Basically get the template from your version, glob for "obj" an replace read_some_data – batFINGER Jul 24 '19 at 10:39
  • Ok I better understand know, I am in version 2.79 ! Thanks for your explanations, my problem is now when I want to save it, I can only "save as' and not 'save' so the file is still the original one when I launch Blender. I am sorry but I don't know how to run this new file instead of the existing one.. – art2611 Jul 24 '19 at 14:52
  • Totally confused by this comment, save what? – batFINGER Jul 24 '19 at 15:35
  • Excuse me, hope it will be more clear here. The script you show me is a modification of "Operator Import" file isn't it ? But even if I have an access to this file from the text editor, I don't know how to modify the script permanently (So save the new version to replace the existing version) – art2611 Jul 26 '19 at 07:08