diff options
author | Joerg Mueller <nexyon@gmail.com> | 2011-05-29 04:48:03 +0400 |
---|---|---|
committer | Joerg Mueller <nexyon@gmail.com> | 2011-05-29 04:48:03 +0400 |
commit | 9cfd093a934097a3054f89b70168fa6ed5be695e (patch) | |
tree | c9212d55e88b7d626c108894c2113eed29513d5b /release | |
parent | 07dca944766e702453730fe1a55605dab9380c6e (diff) | |
parent | fc3904d7b3189e4d201666464d71feba1619a36e (diff) |
Merge with trunk r36987.
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/bpy_extras/image_utils.py | 91 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/io_utils.py | 78 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/mesh_utils.py | 376 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/object_utils.py | 30 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/view3d_utils.py | 14 | ||||
-rw-r--r-- | release/scripts/modules/bpy_types.py | 157 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/mesh.py | 3 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/uvcalc_follow_active.py | 4 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/wm.py | 8 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/__init__.py | 36 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_data_empty.py | 4 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_object_constraint.py | 4 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_image.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_userpref.py | 3 | ||||
-rw-r--r-- | release/windows/installer/00.sconsblender.nsi | 81 |
15 files changed, 597 insertions, 294 deletions
diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py index 39e49ee1f96..a7d0226fa23 100644 --- a/release/scripts/modules/bpy_extras/image_utils.py +++ b/release/scripts/modules/bpy_extras/image_utils.py @@ -18,10 +18,89 @@ # <pep8 compliant> +__all__ = ( + "load_image", +) -def image_load(filepath, dirpath, place_holder=False, recursive=False, convert_callback=None): - import bpy - try: - return bpy.data.images.load(filepath) - except RuntimeError: - return bpy.data.images.new("Untitled", 128, 128) +# limited replacement for BPyImage.comprehensiveImageLoad +def load_image(imagepath, + dirname="", + place_holder=False, + recursive=False, + ncase_cmp=True, + convert_callback=None, + verbose=False, + ): + """ + Return an image from the file path with options to search multiple paths and + return a placeholder if its not found. + + :arg filepath: The image filename + If a path precedes it, this will be searched as well. + :type filepath: string + :arg dirname: is the directory where the image may be located - any file at + the end will be ignored. + :type dirname: string + :arg place_holder: if True a new place holder image will be created. + this is usefull so later you can relink the image to its original data. + :type place_holder: bool + :arg recursive: If True, directories will be recursivly searched. + Be carefull with this if you have files in your root directory because + it may take a long time. + :type recursive: bool + :arg ncase_cmp: on non windows systems, find the correct case for the file. + :type ncase_cmp: bool + :arg convert_callback: a function that takes an existing path and returns a new one. + Use this when loading image formats blender may not support, the CONVERT_CALLBACK + can take the path for a GIF (for example), convert it to a PNG and return the PNG's path. + For formats blender can read, simply return the path that is given. + :type convert_callback: function + :return: an image or None + :rtype: :class:`Image` + """ + import os + + # TODO: recursive + + def _image_load(path): + import bpy + + if convert_callback: + path = convert_callback(path) + + image = bpy.data.images.load(path) + + if verbose: + print(" image loaded '%s'" % path) + + return image + + if verbose: + print("load_image('%s', '%s', ...)" % (imagepath, dirname)) + + if os.path.exists(imagepath): + return _image_load(imagepath) + + variants = [imagepath] + + if dirname: + variants += [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] + + for filepath_test in variants: + if ncase_cmp: + ncase_variants = filepath_test, bpy.path.resolve_ncase(filepath) + else: + ncase_variants = (filepath_test, ) + + for nfilepath in ncase_variants: + if os.path.exists(nfilepath): + return _image_load(nfilepath) + + if place_holder: + image = bpy.data.images.new(os.path.basename(filepath), 128, 128) + # allow the path to be resolved later + image.filepath = imagepath + return image + + # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + return None diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 820d7cfa39d..c444fd618a8 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -18,6 +18,19 @@ # <pep8 compliant> +__all__ = ( + "ExportHelper", + "ImportHelper", + "axis_conversion", + "create_derived_objects", + "free_derived_objects", + "unpack_list", + "unpack_face_list", + "path_reference", + "path_reference_copy", + "path_reference_mode", +) + import bpy from bpy.props import StringProperty, BoolProperty, EnumProperty @@ -101,29 +114,29 @@ _axis_convert_matrix = ( # where all 4 values are or'd into a single value... # (i1<<0 | i1<<3 | i1<<6 | i1<<9) _axis_convert_lut = ( - {0x5c, 0x9a, 0x119, 0x15d, 0x20b, 0x2a2, 0x2c8, 0x365, 0x413, 0x46c, 0x4d0, 0x529, 0x644, 0x682, 0x701, 0x745, 0x823, 0x88a, 0x8e0, 0x94d, 0xa2b, 0xa54, 0xae8, 0xb11}, - {0x9c, 0xac, 0x159, 0x169, 0x22b, 0x2e8, 0x40b, 0x465, 0x4c8, 0x522, 0x684, 0x694, 0x741, 0x751, 0x813, 0x8d0, 0xa23, 0xa4d, 0xae0, 0xb0a}, - {0x99, 0xa9, 0x15c, 0x16c, 0x213, 0x2d0, 0x423, 0x44a, 0x4e0, 0x50d, 0x681, 0x691, 0x744, 0x754, 0x82b, 0x8e8, 0xa0b, 0xa62, 0xac8, 0xb25}, - {0x59, 0x85, 0x11c, 0x142, 0x223, 0x28d, 0x2e0, 0x34a, 0x42b, 0x469, 0x4e8, 0x52c, 0x641, 0x69d, 0x704, 0x75a, 0x80b, 0x8a5, 0x8c8, 0x962, 0xa13, 0xa51, 0xad0, 0xb14}, - {0xa5, 0x162, 0x21c, 0x285, 0x2d9, 0x342, 0x463, 0x46b, 0x520, 0x528, 0x68d, 0x74a, 0x804, 0x89d, 0x8c1, 0x95a, 0xa4b, 0xa53, 0xb08, 0xb10}, - {0x4b, 0x53, 0x108, 0x110, 0x29c, 0x2ac, 0x359, 0x369, 0x41a, 0x422, 0x4dd, 0x4e5, 0x663, 0x66b, 0x720, 0x728, 0x884, 0x894, 0x941, 0x951, 0xa02, 0xa0a, 0xac5, 0xacd}, - {0x63, 0x6b, 0x120, 0x128, 0x299, 0x2a9, 0x35c, 0x36c, 0x405, 0x40d, 0x4c2, 0x4ca, 0x64b, 0x653, 0x708, 0x710, 0x881, 0x891, 0x944, 0x954, 0xa1d, 0xa25, 0xada, 0xae2}, - {0x8a, 0x14d, 0x219, 0x29a, 0x2dc, 0x35d, 0x44b, 0x453, 0x508, 0x510, 0x6a2, 0x765, 0x801, 0x882, 0x8c4, 0x945, 0xa63, 0xa6b, 0xb20, 0xb28}, - {0x5a, 0x62, 0x8b, 0x11d, 0x125, 0x148, 0x22c, 0x28b, 0x293, 0x2e9, 0x348, 0x350, 0x41c, 0x42c, 0x45a, 0x4d9, 0x4e9, 0x51d, 0x642, 0x64a, 0x6a3, 0x705, 0x70d, 0x760, 0x814, 0x8a3, 0x8ab, 0x8d1, 0x960, 0x968, 0xa04, 0xa14, 0xa42, 0xac1, 0xad1, 0xb05}, - {0x54, 0xab, 0x111, 0x168, 0x21d, 0x225, 0x2da, 0x2e2, 0x45c, 0x519, 0x66c, 0x693, 0x729, 0x750, 0x805, 0x80d, 0x8c2, 0x8ca, 0xa44, 0xb01}, - {0x51, 0x93, 0x114, 0x150, 0x202, 0x20a, 0x2c5, 0x2cd, 0x459, 0x51c, 0x669, 0x6ab, 0x72c, 0x768, 0x81a, 0x822, 0x8dd, 0x8e5, 0xa41, 0xb04}, - {0x45, 0x4d, 0xa3, 0x102, 0x10a, 0x160, 0x229, 0x2a3, 0x2ab, 0x2ec, 0x360, 0x368, 0x419, 0x429, 0x445, 0x4dc, 0x4ec, 0x502, 0x65d, 0x665, 0x68b, 0x71a, 0x722, 0x748, 0x811, 0x88b, 0x893, 0x8d4, 0x948, 0x950, 0xa01, 0xa11, 0xa5d, 0xac4, 0xad4, 0xb1a}, - {0x5d, 0x65, 0xa0, 0x11a, 0x122, 0x163, 0x214, 0x2a0, 0x2a8, 0x2d1, 0x363, 0x36b, 0x404, 0x414, 0x45d, 0x4c1, 0x4d1, 0x51a, 0x645, 0x64d, 0x688, 0x702, 0x70a, 0x74b, 0x82c, 0x888, 0x890, 0x8e9, 0x94b, 0x953, 0xa1c, 0xa2c, 0xa45, 0xad9, 0xae9, 0xb02}, - {0x6c, 0x90, 0x129, 0x153, 0x21a, 0x222, 0x2dd, 0x2e5, 0x444, 0x501, 0x654, 0x6a8, 0x711, 0x76b, 0x802, 0x80a, 0x8c5, 0x8cd, 0xa5c, 0xb19}, - {0x69, 0xa8, 0x12c, 0x16b, 0x205, 0x20d, 0x2c2, 0x2ca, 0x441, 0x504, 0x651, 0x690, 0x714, 0x753, 0x81d, 0x825, 0x8da, 0x8e2, 0xa59, 0xb1c}, - {0x42, 0x4a, 0x88, 0x105, 0x10d, 0x14b, 0x211, 0x288, 0x290, 0x2d4, 0x34b, 0x353, 0x401, 0x411, 0x442, 0x4c4, 0x4d4, 0x505, 0x65a, 0x662, 0x6a0, 0x71d, 0x725, 0x763, 0x829, 0x8a0, 0x8a8, 0x8ec, 0x963, 0x96b, 0xa19, 0xa29, 0xa5a, 0xadc, 0xaec, 0xb1d}, - {0xa2, 0x165, 0x204, 0x282, 0x2c1, 0x345, 0x448, 0x450, 0x50b, 0x513, 0x68a, 0x74d, 0x81c, 0x89a, 0x8d9, 0x95d, 0xa60, 0xa68, 0xb23, 0xb2b}, - {0x60, 0x68, 0x123, 0x12b, 0x284, 0x294, 0x341, 0x351, 0x41d, 0x425, 0x4da, 0x4e2, 0x648, 0x650, 0x70b, 0x713, 0x89c, 0x8ac, 0x959, 0x969, 0xa05, 0xa0d, 0xac2, 0xaca}, - {0x48, 0x50, 0x10b, 0x113, 0x281, 0x291, 0x344, 0x354, 0x402, 0x40a, 0x4c5, 0x4cd, 0x660, 0x668, 0x723, 0x72b, 0x899, 0x8a9, 0x95c, 0x96c, 0xa1a, 0xa22, 0xadd, 0xae5}, - {0x8d, 0x14a, 0x201, 0x29d, 0x2c4, 0x35a, 0x460, 0x468, 0x523, 0x52b, 0x6a5, 0x762, 0x819, 0x885, 0x8dc, 0x942, 0xa48, 0xa50, 0xb0b, 0xb13}, - {0x44, 0x9d, 0x101, 0x15a, 0x220, 0x2a5, 0x2e3, 0x362, 0x428, 0x454, 0x4eb, 0x511, 0x65c, 0x685, 0x719, 0x742, 0x808, 0x88d, 0x8cb, 0x94a, 0xa10, 0xa6c, 0xad3, 0xb29}, - {0x84, 0x94, 0x141, 0x151, 0x210, 0x2d3, 0x420, 0x462, 0x4e3, 0x525, 0x69c, 0x6ac, 0x759, 0x769, 0x828, 0x8eb, 0xa08, 0xa4a, 0xacb, 0xb0d}, - {0x81, 0x91, 0x144, 0x154, 0x228, 0x2eb, 0x408, 0x44d, 0x4cb, 0x50a, 0x699, 0x6a9, 0x75c, 0x76c, 0x810, 0x8d3, 0xa20, 0xa65, 0xae3, 0xb22}, + {0x8C8, 0x4D0, 0x2E0, 0xAE8, 0x701, 0x511, 0x119, 0xB29, 0x682, 0x88A, 0x09A, 0x2A2, 0x80B, 0x413, 0x223, 0xA2B, 0x644, 0x454, 0x05C, 0xA6C, 0x745, 0x94D, 0x15D, 0x365}, + {0xAC8, 0x8D0, 0x4E0, 0x2E8, 0x741, 0x951, 0x159, 0x369, 0x702, 0xB0A, 0x11A, 0x522, 0xA0B, 0x813, 0x423, 0x22B, 0x684, 0x894, 0x09C, 0x2AC, 0x645, 0xA4D, 0x05D, 0x465}, + {0x4C8, 0x2D0, 0xAE0, 0x8E8, 0x681, 0x291, 0x099, 0x8A9, 0x642, 0x44A, 0x05A, 0xA62, 0x40B, 0x213, 0xA23, 0x82B, 0x744, 0x354, 0x15C, 0x96C, 0x705, 0x50D, 0x11D, 0xB25}, + {0x2C8, 0xAD0, 0x8E0, 0x4E8, 0x641, 0xA51, 0x059, 0x469, 0x742, 0x34A, 0x15A, 0x962, 0x20B, 0xA13, 0x823, 0x42B, 0x704, 0xB14, 0x11C, 0x52C, 0x685, 0x28D, 0x09D, 0x8A5}, + {0x708, 0xB10, 0x120, 0x528, 0x8C1, 0xAD1, 0x2D9, 0x4E9, 0x942, 0x74A, 0x35A, 0x162, 0x64B, 0xA53, 0x063, 0x46B, 0x804, 0xA14, 0x21C, 0x42C, 0x885, 0x68D, 0x29D, 0x0A5}, + {0xB08, 0x110, 0x520, 0x728, 0x941, 0x151, 0x359, 0x769, 0x802, 0xA0A, 0x21A, 0x422, 0xA4B, 0x053, 0x463, 0x66B, 0x884, 0x094, 0x29C, 0x6AC, 0x8C5, 0xACD, 0x2DD, 0x4E5}, + {0x508, 0x710, 0xB20, 0x128, 0x881, 0x691, 0x299, 0x0A9, 0x8C2, 0x4CA, 0x2DA, 0xAE2, 0x44B, 0x653, 0xA63, 0x06B, 0x944, 0x754, 0x35C, 0x16C, 0x805, 0x40D, 0x21D, 0xA25}, + {0x108, 0x510, 0x720, 0xB28, 0x801, 0x411, 0x219, 0xA29, 0x882, 0x08A, 0x29A, 0x6A2, 0x04B, 0x453, 0x663, 0xA6B, 0x8C4, 0x4D4, 0x2DC, 0xAEC, 0x945, 0x14D, 0x35D, 0x765}, + {0x748, 0x350, 0x160, 0x968, 0xAC1, 0x2D1, 0x4D9, 0x8E9, 0xA42, 0x64A, 0x45A, 0x062, 0x68B, 0x293, 0x0A3, 0x8AB, 0xA04, 0x214, 0x41C, 0x82C, 0xB05, 0x70D, 0x51D, 0x125}, + {0x948, 0x750, 0x360, 0x168, 0xB01, 0x711, 0x519, 0x129, 0xAC2, 0x8CA, 0x4DA, 0x2E2, 0x88B, 0x693, 0x2A3, 0x0AB, 0xA44, 0x654, 0x45C, 0x06C, 0xA05, 0x80D, 0x41D, 0x225}, + {0x348, 0x150, 0x960, 0x768, 0xA41, 0x051, 0x459, 0x669, 0xA02, 0x20A, 0x41A, 0x822, 0x28B, 0x093, 0x8A3, 0x6AB, 0xB04, 0x114, 0x51C, 0x72C, 0xAC5, 0x2CD, 0x4DD, 0x8E5}, + {0x148, 0x950, 0x760, 0x368, 0xA01, 0x811, 0x419, 0x229, 0xB02, 0x10A, 0x51A, 0x722, 0x08B, 0x893, 0x6A3, 0x2AB, 0xAC4, 0x8D4, 0x4DC, 0x2EC, 0xA45, 0x04D, 0x45D, 0x665}, + {0x688, 0x890, 0x0A0, 0x2A8, 0x4C1, 0x8D1, 0xAD9, 0x2E9, 0x502, 0x70A, 0xB1A, 0x122, 0x74B, 0x953, 0x163, 0x36B, 0x404, 0x814, 0xA1C, 0x22C, 0x445, 0x64D, 0xA5D, 0x065}, + {0x888, 0x090, 0x2A0, 0x6A8, 0x501, 0x111, 0xB19, 0x729, 0x402, 0x80A, 0xA1A, 0x222, 0x94B, 0x153, 0x363, 0x76B, 0x444, 0x054, 0xA5C, 0x66C, 0x4C5, 0x8CD, 0xADD, 0x2E5}, + {0x288, 0x690, 0x8A0, 0x0A8, 0x441, 0x651, 0xA59, 0x069, 0x4C2, 0x2CA, 0xADA, 0x8E2, 0x34B, 0x753, 0x963, 0x16B, 0x504, 0x714, 0xB1C, 0x12C, 0x405, 0x20D, 0xA1D, 0x825}, + {0x088, 0x290, 0x6A0, 0x8A8, 0x401, 0x211, 0xA19, 0x829, 0x442, 0x04A, 0xA5A, 0x662, 0x14B, 0x353, 0x763, 0x96B, 0x4C4, 0x2D4, 0xADC, 0x8EC, 0x505, 0x10D, 0xB1D, 0x725}, + {0x648, 0x450, 0x060, 0xA68, 0x2C1, 0x4D1, 0x8D9, 0xAE9, 0x282, 0x68A, 0x89A, 0x0A2, 0x70B, 0x513, 0x123, 0xB2B, 0x204, 0x414, 0x81C, 0xA2C, 0x345, 0x74D, 0x95D, 0x165}, + {0xA48, 0x650, 0x460, 0x068, 0x341, 0x751, 0x959, 0x169, 0x2C2, 0xACA, 0x8DA, 0x4E2, 0xB0B, 0x713, 0x523, 0x12B, 0x284, 0x694, 0x89C, 0x0AC, 0x205, 0xA0D, 0x81D, 0x425}, + {0x448, 0x050, 0xA60, 0x668, 0x281, 0x091, 0x899, 0x6A9, 0x202, 0x40A, 0x81A, 0xA22, 0x50B, 0x113, 0xB23, 0x72B, 0x344, 0x154, 0x95C, 0x76C, 0x2C5, 0x4CD, 0x8DD, 0xAE5}, + {0x048, 0xA50, 0x660, 0x468, 0x201, 0xA11, 0x819, 0x429, 0x342, 0x14A, 0x95A, 0x762, 0x10B, 0xB13, 0x723, 0x52B, 0x2C4, 0xAD4, 0x8DC, 0x4EC, 0x285, 0x08D, 0x89D, 0x6A5}, + {0x808, 0xA10, 0x220, 0x428, 0x101, 0xB11, 0x719, 0x529, 0x142, 0x94A, 0x75A, 0x362, 0x8CB, 0xAD3, 0x2E3, 0x4EB, 0x044, 0xA54, 0x65C, 0x46C, 0x085, 0x88D, 0x69D, 0x2A5}, + {0xA08, 0x210, 0x420, 0x828, 0x141, 0x351, 0x759, 0x969, 0x042, 0xA4A, 0x65A, 0x462, 0xACB, 0x2D3, 0x4E3, 0x8EB, 0x084, 0x294, 0x69C, 0x8AC, 0x105, 0xB0D, 0x71D, 0x525}, + {0x408, 0x810, 0xA20, 0x228, 0x081, 0x891, 0x699, 0x2A9, 0x102, 0x50A, 0x71A, 0xB22, 0x4CB, 0x8D3, 0xAE3, 0x2EB, 0x144, 0x954, 0x75C, 0x36C, 0x045, 0x44D, 0x65D, 0xA65}, ) _axis_convert_num = {'X': 0, 'Y': 1, 'Z': 2, '-X': 3, '-Y': 4, '-Z': 5} @@ -141,30 +154,13 @@ def axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z'): return Matrix().to_3x3() value = reduce(int.__or__, (_axis_convert_num[a] << (i * 3) for i, a in enumerate((from_forward, from_up, to_forward, to_up)))) + for i, axis_lut in enumerate(_axis_convert_lut): if value in axis_lut: return Matrix(_axis_convert_matrix[i]) assert("internal error") -# limited replacement for BPyImage.comprehensiveImageLoad -def load_image(imagepath, dirname): - import os - - if os.path.exists(imagepath): - return bpy.data.images.load(imagepath) - - variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] - - for filepath in variants: - for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)): - if os.path.exists(nfilepath): - return bpy.data.images.load(nfilepath) - - # TODO comprehensiveImageLoad also searched in bpy.config.textureDir - return None - - # return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() def create_derived_objects(scene, ob): if ob.parent and ob.parent.dupli_type != 'NONE': diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py index 5bacff7b0cc..b6d8a1fcf16 100644 --- a/release/scripts/modules/bpy_extras/mesh_utils.py +++ b/release/scripts/modules/bpy_extras/mesh_utils.py @@ -18,13 +18,25 @@ # <pep8 compliant> +__all__ = ( + "mesh_linked_faces", + "edge_face_count_dict", + "edge_face_count", + "edge_loops_from_faces", + "edge_loops_from_edges", + "ngon_tesselate", +) def mesh_linked_faces(mesh): - ''' - Splits the mesh into connected parts, - these parts are returned as lists of faces. - used for seperating cubes from other mesh elements in the 1 mesh - ''' + """ + Splits the mesh into connected faces, use this for seperating cubes from + other mesh elements within 1 mesh datablock. + + :arg mesh: the mesh used to group with. + :type mesh: :class:`Mesh` + :return: lists of lists containing faces. + :rtype: list + """ # Build vert face connectivity vert_faces = [[] for i in range(len(mesh.vertices))] @@ -67,3 +79,357 @@ def mesh_linked_faces(mesh): # return all face groups that are not null # this is all the faces that are connected in their own lists. return [fg for fg in face_groups if fg] + + +def edge_face_count_dict(mesh): + """ + :return: dict of edge keys with their value set to the number of + faces using each edge. + :rtype: dict + """ + face_edge_keys = [face.edge_keys for face in mesh.faces] + face_edge_count = {} + for face_keys in face_edge_keys: + for key in face_keys: + try: + face_edge_count[key] += 1 + except: + face_edge_count[key] = 1 + + return face_edge_count + + +def edge_face_count(mesh): + """ + :return: list face users for each item in mesh.edges. + :rtype: list + """ + edge_face_count_dict = edge_face_count_dict(mesh) + get = dict.get + return [get(edge_face_count_dict, ed.key, 0) for ed in mesh.edges] + + +def edge_loops_from_faces(mesh, faces=None, seams=()): + """ + Edge loops defined by faces + + Takes me.faces or a list of faces and returns the edge loops + These edge loops are the edges that sit between quads, so they dont touch + 1 quad, note: not connected will make 2 edge loops, + both only containing 2 edges. + + return a list of edge key lists + [[(0, 1), (4, 8), (3, 8)], ...] + + :arg mesh: the mesh used to get edge loops from. + :type mesh: :class:`Mesh` + :arg faces: optional face list to only use some of the meshes faces. + :type faces: :class:`MeshFaces`, sequence or or NoneType + :return: return a list of edge vertex index lists. + :rtype: list + """ + + OTHER_INDEX = 2, 3, 0, 1 # opposite face index + + if faces is None: + faces = mesh.faces + + edges = {} + + for f in faces: +# if len(f) == 4: + if f.vertices_raw[3] != 0: + edge_keys = f.edge_keys + for i, edkey in enumerate(f.edge_keys): + edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]]) + + for edkey in seams: + edges[edkey] = [] + + # Collect edge loops here + edge_loops = [] + + for edkey, ed_adj in edges.items(): + if 0 < len(ed_adj) < 3: # 1 or 2 + # Seek the first edge + context_loop = [edkey, ed_adj[0]] + edge_loops.append(context_loop) + if len(ed_adj) == 2: + other_dir = ed_adj[1] + else: + other_dir = None + + ed_adj[:] = [] + + flipped = False + + while 1: + # from knowing the last 2, look for th next. + ed_adj = edges[context_loop[-1]] + if len(ed_adj) != 2: + + if other_dir and flipped == False: # the original edge had 2 other edges + flipped = True # only flip the list once + context_loop.reverse() + ed_adj[:] = [] + context_loop.append(other_dir) # save 1 lookiup + + ed_adj = edges[context_loop[-1]] + if len(ed_adj) != 2: + ed_adj[:] = [] + break + else: + ed_adj[:] = [] + break + + i = ed_adj.index(context_loop[-2]) + context_loop.append(ed_adj[not i]) + + # Dont look at this again + ed_adj[:] = [] + + return edge_loops + + +def edge_loops_from_edges(mesh, edges=None): + """ + Edge loops defined by edges + + Takes me.edges or a list of edges and returns the edge loops + + return a list of vertex indices. + [ [1, 6, 7, 2], ...] + + closed loops have matching start and end values. + """ + line_polys = [] + + # Get edges not used by a face + if edges is None: + edges = mesh.edges + + if not hasattr(edges, "pop"): + edges = edges[:] + + edge_dict = {ed.key: ed for ed in mesh.edges if ed.select} + + while edges: + current_edge = edges.pop() + vert_end, vert_start = current_edge.vertices[:] + line_poly = [vert_start, vert_end] + + ok = True + while ok: + ok = False + #for i, ed in enumerate(edges): + i = len(edges) + while i: + i -= 1 + ed = edges[i] + v1, v2 = ed.vertices + if v1 == vert_end: + line_poly.append(v2) + vert_end = line_poly[-1] + ok = 1 + del edges[i] + # break + elif v2 == vert_end: + line_poly.append(v1) + vert_end = line_poly[-1] + ok = 1 + del edges[i] + #break + elif v1 == vert_start: + line_poly.insert(0, v2) + vert_start = line_poly[0] + ok = 1 + del edges[i] + # break + elif v2 == vert_start: + line_poly.insert(0, v1) + vert_start = line_poly[0] + ok = 1 + del edges[i] + #break + line_polys.append(line_poly) + + return line_polys + + +def ngon_tesselate(from_data, indices, fix_loops=True): + ''' + Takes a polyline of indices (fgon) + and returns a list of face indicie lists. + Designed to be used for importers that need indices for an fgon to create from existing verts. + + from_data: either a mesh, or a list/tuple of vectors. + indices: a list of indices to use this list is the ordered closed polyline to fill, and can be a subset of the data given. + fix_loops: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. + ''' + + from mathutils import Vector + vector_to_tuple = Vector.to_tuple + + if not indices: + return [] + + def mlen(co): + return abs(co[0]) + abs(co[1]) + abs(co[2]) # manhatten length of a vector, faster then length + + def vert_treplet(v, i): + return v, vector_to_tuple(v, 6), i, mlen(v) + + def ed_key_mlen(v1, v2): + if v1[3] > v2[3]: + return v2[1], v1[1] + else: + return v1[1], v2[1] + + if not PREF_FIX_LOOPS: + ''' + Normal single concave loop filling + ''' + if type(from_data) in (tuple, list): + verts = [Vector(from_data[i]) for ii, i in enumerate(indices)] + else: + verts = [from_data.vertices[i].co for ii, i in enumerate(indices)] + + for i in range(len(verts) - 1, 0, -1): # same as reversed(xrange(1, len(verts))): + if verts[i][1] == verts[i - 1][0]: + verts.pop(i - 1) + + fill = fill_polygon([verts]) + + else: + ''' + Seperate this loop into multiple loops be finding edges that are used twice + This is used by lightwave LWO files a lot + ''' + + if type(from_data) in (tuple, list): + verts = [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)] + else: + verts = [vert_treplet(from_data.vertices[i].co, ii) for ii, i in enumerate(indices)] + + edges = [(i, i - 1) for i in range(len(verts))] + if edges: + edges[0] = (0, len(verts) - 1) + + if not verts: + return [] + + edges_used = set() + edges_doubles = set() + # We need to check if any edges are used twice location based. + for ed in edges: + edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]]) + if edkey in edges_used: + edges_doubles.add(edkey) + else: + edges_used.add(edkey) + + # Store a list of unconnected loop segments split by double edges. + # will join later + loop_segments = [] + + v_prev = verts[0] + context_loop = [v_prev] + loop_segments = [context_loop] + + for v in verts: + if v != v_prev: + # Are we crossing an edge we removed? + if ed_key_mlen(v, v_prev) in edges_doubles: + context_loop = [v] + loop_segments.append(context_loop) + else: + if context_loop and context_loop[-1][1] == v[1]: + #raise "as" + pass + else: + context_loop.append(v) + + v_prev = v + # Now join loop segments + + def join_seg(s1, s2): + if s2[-1][1] == s1[0][1]: + s1, s2 = s2, s1 + elif s1[-1][1] == s2[0][1]: + pass + else: + return False + + # If were stuill here s1 and s2 are 2 segments in the same polyline + s1.pop() # remove the last vert from s1 + s1.extend(s2) # add segment 2 to segment 1 + + if s1[0][1] == s1[-1][1]: # remove endpoints double + s1.pop() + + s2[:] = [] # Empty this segment s2 so we dont use it again. + return True + + joining_segments = True + while joining_segments: + joining_segments = False + segcount = len(loop_segments) + + for j in range(segcount - 1, -1, -1): # reversed(range(segcount)): + seg_j = loop_segments[j] + if seg_j: + for k in range(j - 1, -1, -1): # reversed(range(j)): + if not seg_j: + break + seg_k = loop_segments[k] + + if seg_k and join_seg(seg_j, seg_k): + joining_segments = True + + loop_list = loop_segments + + for verts in loop_list: + while verts and verts[0][1] == verts[-1][1]: + verts.pop() + + loop_list = [verts for verts in loop_list if len(verts) > 2] + # DONE DEALING WITH LOOP FIXING + + # vert mapping + vert_map = [None] * len(indices) + ii = 0 + for verts in loop_list: + if len(verts) > 2: + for i, vert in enumerate(verts): + vert_map[i + ii] = vert[2] + ii += len(verts) + + fill = tesselate_polygon([[v[0] for v in loop] for loop in loop_list]) + #draw_loops(loop_list) + #raise 'done loop' + # map to original indices + fill = [[vert_map[i] for i in reversed(f)] for f in fill] + + if not fill: + print('Warning Cannot scanfill, fallback on a triangle fan.') + fill = [[0, i - 1, i] for i in range(2, len(indices))] + else: + # Use real scanfill. + # See if its flipped the wrong way. + flip = None + for fi in fill: + if flip != None: + break + for i, vi in enumerate(fi): + if vi == 0 and fi[i - 1] == 1: + flip = False + break + elif vi == 1 and fi[i - 1] == 0: + flip = True + break + + if not flip: + for i, fi in enumerate(fill): + fill[i] = tuple([ii for ii in reversed(fi)]) + + return fill diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 1cf7fc2f4d5..51a8d4b5e23 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -18,11 +18,27 @@ # <pep8 compliant> +__all__ = ( + "add_object_align_init", + "object_data_add", +) + + import bpy import mathutils def add_object_align_init(context, operator): + """ + Return a matrix using the operator settings and view context. + + :arg context: The context to use. + :type context: :class:`Context` + :arg operator: The operator, checked for location and rotation properties. + :type operator: :class:`Operator` + :return: the matrix from the context and settings. + :rtype: :class:`Matrix` + """ space_data = context.space_data if space_data.type != 'VIEW_3D': space_data = None @@ -64,7 +80,19 @@ def add_object_align_init(context, operator): def object_data_add(context, obdata, operator=None): - + """ + Add an object using the view context and preference to to initialize the + location, rotation and layer. + + :arg context: The context to use. + :type context: :class:`Context` + :arg obdata: the data used for the new object. + :type obdata: valid object data type or None. + :arg operator: The operator, checked for location and rotation properties. + :type operator: :class:`Operator` + :return: the newly created object in the scene. + :rtype: :class:`ObjectBase` + """ scene = context.scene # ugh, could be made nicer diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py index 7d37b94982f..45f537ebd2f 100644 --- a/release/scripts/modules/bpy_extras/view3d_utils.py +++ b/release/scripts/modules/bpy_extras/view3d_utils.py @@ -18,6 +18,12 @@ # <pep8 compliant> +__all__ = ( + "region_2d_to_vector_3d", + "region_2d_to_location_3d", + "location_3d_to_region_2d", + "location_3d_to_region_2d", +) def region_2d_to_vector_3d(region, rv3d, coord): """ @@ -28,7 +34,7 @@ def region_2d_to_vector_3d(region, rv3d, coord): :type region: :class:`Region` :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d. :type rv3d: :class:`RegionView3D` - :arg coord: 2d coordinates relative to the region; + :arg coord: 2d coordinates relative to the region: (event.mouse_region_x, event.mouse_region_y) for example. :type coord: 2d vector :return: normalized 3d vector. @@ -44,8 +50,10 @@ def region_2d_to_vector_3d(region, rv3d, coord): -0.5 )) - w = (out[0] * persinv[0][3]) + (out[1] * persinv[1][3]) + (out[2] * persinv[2][3]) + persinv[3][3] - + w = (out[0] * persinv[0][3]) + \ + (out[1] * persinv[1][3]) + \ + (out[2] * persinv[2][3]) + persinv[3][3] + return ((out * persinv) / w) - rv3d.view_matrix.inverted()[3].xyz else: return rv3d.view_matrix.inverted()[2].xyz.normalized() diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index c3352dd33ad..3c1b454e72e 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -356,163 +356,6 @@ class Mesh(bpy_types.ID): def edge_keys(self): return [edge_key for face in self.faces for edge_key in face.edge_keys] - @property - def edge_face_count_dict(self): - face_edge_keys = [face.edge_keys for face in self.faces] - face_edge_count = {} - for face_keys in face_edge_keys: - for key in face_keys: - try: - face_edge_count[key] += 1 - except: - face_edge_count[key] = 1 - - return face_edge_count - - @property - def edge_face_count(self): - edge_face_count_dict = self.edge_face_count_dict - return [edge_face_count_dict.get(ed.key, 0) for ed in self.edges] - - def edge_loops_from_faces(self, faces=None, seams=()): - """ - Edge loops defined by faces - - Takes me.faces or a list of faces and returns the edge loops - These edge loops are the edges that sit between quads, so they dont touch - 1 quad, note: not connected will make 2 edge loops, both only containing 2 edges. - - return a list of edge key lists - [ [(0,1), (4, 8), (3,8)], ...] - - return a list of edge vertex index lists - """ - - OTHER_INDEX = 2, 3, 0, 1 # opposite face index - - if faces is None: - faces = self.faces - - edges = {} - - for f in faces: -# if len(f) == 4: - if f.vertices_raw[3] != 0: - edge_keys = f.edge_keys - for i, edkey in enumerate(f.edge_keys): - edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]]) - - for edkey in seams: - edges[edkey] = [] - - # Collect edge loops here - edge_loops = [] - - for edkey, ed_adj in edges.items(): - if 0 < len(ed_adj) < 3: # 1 or 2 - # Seek the first edge - context_loop = [edkey, ed_adj[0]] - edge_loops.append(context_loop) - if len(ed_adj) == 2: - other_dir = ed_adj[1] - else: - other_dir = None - - ed_adj[:] = [] - - flipped = False - - while 1: - # from knowing the last 2, look for th next. - ed_adj = edges[context_loop[-1]] - if len(ed_adj) != 2: - - if other_dir and flipped == False: # the original edge had 2 other edges - flipped = True # only flip the list once - context_loop.reverse() - ed_adj[:] = [] - context_loop.append(other_dir) # save 1 lookiup - - ed_adj = edges[context_loop[-1]] - if len(ed_adj) != 2: - ed_adj[:] = [] - break - else: - ed_adj[:] = [] - break - - i = ed_adj.index(context_loop[-2]) - context_loop.append(ed_adj[not i]) - - # Dont look at this again - ed_adj[:] = [] - - return edge_loops - - def edge_loops_from_edges(self, edges=None): - """ - Edge loops defined by edges - - Takes me.edges or a list of edges and returns the edge loops - - return a list of vertex indices. - [ [1, 6, 7, 2], ...] - - closed loops have matching start and end values. - """ - line_polys = [] - - # Get edges not used by a face - if edges is None: - edges = self.edges - - if not hasattr(edges, "pop"): - edges = edges[:] - - edge_dict = {ed.key: ed for ed in self.edges if ed.select} - - while edges: - current_edge = edges.pop() - vert_end, vert_start = current_edge.vertices[:] - line_poly = [vert_start, vert_end] - - ok = True - while ok: - ok = False - #for i, ed in enumerate(edges): - i = len(edges) - while i: - i -= 1 - ed = edges[i] - v1, v2 = ed.vertices - if v1 == vert_end: - line_poly.append(v2) - vert_end = line_poly[-1] - ok = 1 - del edges[i] - # break - elif v2 == vert_end: - line_poly.append(v1) - vert_end = line_poly[-1] - ok = 1 - del edges[i] - #break - elif v1 == vert_start: - line_poly.insert(0, v2) - vert_start = line_poly[0] - ok = 1 - del edges[i] - # break - elif v2 == vert_start: - line_poly.insert(0, v1) - vert_start = line_poly[0] - ok = 1 - del edges[i] - #break - line_polys.append(line_poly) - - return line_polys - class MeshEdge(StructRNA): __slots__ = () diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py index 44d81ba53df..89802d7ba5c 100644 --- a/release/scripts/startup/bl_operators/mesh.py +++ b/release/scripts/startup/bl_operators/mesh.py @@ -36,6 +36,7 @@ class MeshSelectInteriorFaces(bpy.types.Operator): return (ob and ob.type == 'MESH') def execute(self, context): + from bpy_extras import mesh_utils ob = context.active_object context.tool_settings.mesh_select_mode = False, False, True is_editmode = (ob.mode == 'EDIT') @@ -47,7 +48,7 @@ class MeshSelectInteriorFaces(bpy.types.Operator): face_list = mesh.faces[:] face_edge_keys = [face.edge_keys for face in face_list] - edge_face_count = mesh.edge_face_count_dict + edge_face_count = mesh_utils.edge_face_count_dict(mesh) def test_interior(index): for key in face_edge_keys[index]: diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py index ad5ec15ff80..edd09d9c66b 100644 --- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py +++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py @@ -25,6 +25,8 @@ import bpy def extend(obj, operator, EXTEND_MODE): + from bpy_extras import mesh_utils + me = obj.data me_verts = me.vertices # script will fail without UVs @@ -170,7 +172,7 @@ def extend(obj, operator, EXTEND_MODE): edge_faces[edkey] = [i] if EXTEND_MODE == 'LENGTH': - edge_loops = me.edge_loops_from_faces(face_sel, [ed.key for ed in me.edges if ed.use_seam]) + edge_loops = mesh_utils.edge_loops_from_faces(me, face_sel, [ed.key for ed in me.edges if ed.use_seam]) me_verts = me.vertices for loop in edge_loops: looplen = [0.0] diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 53c8d562297..3f4a061c4ac 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -940,6 +940,14 @@ class WM_OT_copy_prev_settings(bpy.types.Operator): self.report({'ERROR'}, "Source path %r exists" % path_src) else: shutil.copytree(path_src, path_dst) + + # in 2.57 and earlier windows installers, system scripts were copied + # into the configuration directory, don't want to copy those + system_script = os.path.join(path_dst, 'scripts/modules/bpy_types.py') + if os.path.isfile(system_script): + shutil.rmtree(os.path.join(path_dst, 'scripts')) + shutil.rmtree(os.path.join(path_dst, 'plugins')) + # dont loose users work if they open the splash later. if bpy.data.is_saved is bpy.data.is_dirty is False: bpy.ops.wm.read_homefile() diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 2f933fb5771..bf63c6071b9 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -85,26 +85,26 @@ def register(): from bpy.props import StringProperty, EnumProperty WindowManager = bpy.types.WindowManager + def addon_filter_items(self, context): + import addon_utils + + items = [('All', "All", ""), + ('Enabled', "Enabled", ""), + ('Disabled', "Disabled", ""), + ] + + items_unique = set() + + for mod in addon_utils.modules(space_userpref.USERPREF_PT_addons._addons_fake_modules): + info = addon_utils.module_bl_info(mod) + items_unique.add(info["category"]) + + items.extend([(cat, cat, "") for cat in sorted(items_unique)]) + return items + WindowManager.addon_search = StringProperty(name="Search", description="Search within the selected filter") WindowManager.addon_filter = EnumProperty( - items=[('All', "All", ""), - ('Enabled', "Enabled", ""), - ('Disabled', "Disabled", ""), - ('3D View', "3D View", ""), - ('Add Curve', "Add Curve", ""), - ('Add Mesh', "Add Mesh", ""), - ('Animation', "Animation", ""), - ('Development', "Development", ""), - ('Game Engine', "Game Engine", ""), - ('Import-Export', "Import-Export", ""), - ('Mesh', "Mesh", ""), - ('Object', "Object", ""), - ('Render', "Render", ""), - ('Rigging', "Rigging", ""), - ('Text Editor', "Text Editor", ""), - ('System', "System", ""), - ('Other', "Other", ""), - ], + items=addon_filter_items, name="Category", description="Filter add-ons by category", ) diff --git a/release/scripts/startup/bl_ui/properties_data_empty.py b/release/scripts/startup/bl_ui/properties_data_empty.py index 80f83e7fabe..5a0d327f90d 100644 --- a/release/scripts/startup/bl_ui/properties_data_empty.py +++ b/release/scripts/startup/bl_ui/properties_data_empty.py @@ -44,9 +44,9 @@ class DATA_PT_empty(DataButtonsPanel, bpy.types.Panel): # layout.template_image(ob, "data", None) layout.template_ID(ob, "data", open="image.open", unlink="image.unlink") - row = layout.row(align = True) + row = layout.row(align=True) row.prop(ob, "color", text="Transparency", index=3, slider=True) - row = layout.row(align = True) + row = layout.row(align=True) row.prop(ob, "empty_image_offset", text="Offset X", index=0) row.prop(ob, "empty_image_offset", text="Offset Y", index=1) diff --git a/release/scripts/startup/bl_ui/properties_object_constraint.py b/release/scripts/startup/bl_ui/properties_object_constraint.py index 900570c9664..70121a12caa 100644 --- a/release/scripts/startup/bl_ui/properties_object_constraint.py +++ b/release/scripts/startup/bl_ui/properties_object_constraint.py @@ -671,9 +671,9 @@ class ConstraintButtonsPanel(): row = col.row() row.prop(con, "map_to_z_from", expand=False, text="") row.label(text=" -> Z") - + split = layout.split() - + col = split.column() col.label(text="Destination:") col.row().prop(con, "map_to", expand=True) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 44a1c814e28..9f69ca17076 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -437,7 +437,7 @@ class IMAGE_PT_game_properties(bpy.types.Panel): rd = context.scene.render sima = context.space_data # display even when not in game mode because these settings effect the 3d view - return (sima and sima.image) # and (rd.engine == 'BLENDER_GAME') + return (sima and sima.image) # and (rd.engine == 'BLENDER_GAME') def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index d4ebae04c34..e34755ae72e 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -889,7 +889,8 @@ class USERPREF_PT_addons(bpy.types.Panel): split = layout.split(percentage=0.2) col = split.column() col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM') - col.prop(context.window_manager, "addon_filter", expand=True) + col.label(text="Categories") + col.prop(context.window_manager, "addon_filter", text="") # , expand=True, too slow with dynamic enum. col.label(text="Supported Level") col.prop(context.window_manager, "addon_support", expand=True) diff --git a/release/windows/installer/00.sconsblender.nsi b/release/windows/installer/00.sconsblender.nsi index 03f62f0df48..42a9b1c13b6 100644 --- a/release/windows/installer/00.sconsblender.nsi +++ b/release/windows/installer/00.sconsblender.nsi @@ -33,11 +33,11 @@ RequestExecutionLevel admin !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY -Page custom DataLocation DataLocationOnLeave !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_WELCOME +UninstPage custom un.OptionalRemoveConfig un.OptionalRemoveConfigOnLeave !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH @@ -62,7 +62,6 @@ UninstallIcon "[RELDIR]\00.installer.ico" LangString DESC_StartMenu ${LANG_ENGLISH} "Add shortcut items to the Start Menu. (Recommended)" LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Add a shortcut to Blender on your desktop." LangString DESC_BlendRegister ${LANG_ENGLISH} "Blender can register itself with .blend files to allow double-clicking from Windows Explorer, etc." - LangString TEXT_IO_TITLE ${LANG_ENGLISH} "Specify User Data Location" ;-------------------------------- ;Data @@ -76,15 +75,15 @@ DirText "Use the field below to specify the folder where you want Blender to be SilentUnInstall normal -Var BLENDERHOME Var SHORTVERSION ; This is blender_version_decimal() from path_util.c +Var BLENDERCONFIG +Var REMOVECONFIG ; Custom controls Var HWND -Var HWND_APPDATA -Var HWND_INSTDIR -Var HWND_HOMEDIR +Var HWND_KEEPCONFIG +Var HWND_REMOVECONFIG Function .onInit ClearErrors @@ -103,9 +102,12 @@ Function .onInit FunctionEnd Function un.onInit + SetShellVarContext current + StrCpy $BLENDERCONFIG "$APPDATA\Blender Foundation\Blender" + SetShellVarContext all FunctionEnd -Function DataLocation +Function un.OptionalRemoveConfig nsDialogs::Create /NOUNLOAD 1018 Pop $HWND @@ -113,45 +115,27 @@ Function DataLocation Abort ${EndIf} - ${NSD_CreateLabel} 0 0 100% 24u "Please specify where you wish to install Blender's user data files. Be aware that if you choose to use your Application Data directory, your preferences and scripts will only be accessible by the current user account." - ${NSD_CreateRadioButton} 0 50 100% 12u "Use Application Data directory (recommended)" - Pop $HWND_APPDATA - ${NSD_CreateRadioButton} 0 80 100% 12u "Use installation directory" - Pop $HWND_INSTDIR - ${NSD_CreateRadioButton} 0 110 100% 12u "I have defined a %HOME% variable, please install files there" - Pop $HWND_HOMEDIR - - ${If} ${AtMostWinME} - GetDlgItem $0 $HWND $HWND_APPDATA - EnableWindow $0 0 - SendMessage $HWND_INSTDIR ${BM_SETCHECK} 1 0 - ${Else} - SendMessage $HWND_APPDATA ${BM_SETCHECK} 1 0 - ${EndIf} + ${NSD_CreateRadioButton} 0 50 100% 12u "Keep configuration files, autosaved .blend files and installed addons (recommended)" + Pop $HWND_KEEPCONFIG + ${NSD_CreateRadioButton} 0 80 100% 12u "Remove all files, including configuration files, autosaved .blend files and installed addons" + Pop $HWND_REMOVECONFIG + + SendMessage $HWND_KEEPCONFIG ${BM_SETCHECK} 1 0 nsDialogs::Show FunctionEnd -Function DataLocationOnLeave - ${NSD_GetState} $HWND_APPDATA $R0 +Function un.OptionalRemoveConfigOnLeave + ${NSD_GetState} $HWND_REMOVECONFIG $R0 ${If} $R0 == "1" - SetShellVarContext current - StrCpy $BLENDERHOME "$APPDATA\Blender Foundation\Blender" - SetShellVarContext all + StrCpy $REMOVECONFIG "1" ${Else} - ${NSD_GetState} $HWND_INSTDIR $R0 - ${If} $R0 == "1" - StrCpy $BLENDERHOME $INSTDIR - ${Else} - ${NSD_GetState} $HWND_HOMEDIR $R0 - ${If} $R0 == "1" - ReadEnvStr $BLENDERHOME "HOME" - ${EndIf} - ${EndIf} + StrCpy $REMOVECONFIG "0" ${EndIf} FunctionEnd + Section "Blender [VERSION] (required)" InstallFiles SectionIn RO @@ -160,7 +144,7 @@ Section "Blender [VERSION] (required)" InstallFiles ; The contents of Blender installation root dir [ROOTDIRCONTS] - ; All datafiles (python, scripts, config) + ; All datafiles (python, scripts, datafiles) [DODATAFILES] SetOutPath $INSTDIR @@ -169,7 +153,6 @@ Section "Blender [VERSION] (required)" InstallFiles ${EndIf} ; Write the installation path into the registry WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "Install_Dir" "$INSTDIR" - WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "ConfigData_Dir" "$BLENDERHOME" WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "ShortVersion" "[SHORTVERSION]" ; Write the uninstall keys for Windows WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayName" "Blender" @@ -204,7 +187,7 @@ Section "Open .blend files with Blender" BlendRegister ExecWait '"$INSTDIR\blender.exe" -r' SectionEnd -UninstallText "This will uninstall Blender [VERSION], and all installed files. Before continuing make sure you have created backup of all the files you may want to keep: startup.blend, bookmarks.txt, recent-files.txt. Hit 'Uninstall' to continue." +UninstallText "This will uninstall Blender [VERSION], and all installed files. Hit 'Uninstall' to continue." Section "Uninstall" ; Remove registry keys @@ -212,7 +195,6 @@ Section "Uninstall" SetRegView 64 ${EndIf} - ReadRegStr $BLENDERHOME HKLM "SOFTWARE\BlenderFoundation" "ConfigData_Dir" ReadRegStr $SHORTVERSION HKLM "SOFTWARE\BlenderFoundation" "ShortVersion" DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Blender" DeleteRegKey HKLM "SOFTWARE\BlenderFoundation" @@ -226,21 +208,10 @@ Section "Uninstall" Delete "$INSTDIR\uninstall.exe" - MessageBox MB_YESNO "Recursively erase contents of $BLENDERHOME\$SHORTVERSION\scripts? NOTE: This includes all installed scripts and *any* file and directory you have manually created, installed later or copied. This also including .blend files." IDNO NextNoScriptRemove - RMDir /r "$BLENDERHOME\$SHORTVERSION\scripts" -NextNoScriptRemove: - MessageBox MB_YESNO "Recursively erase contents from $BLENDERHOME\$SHORTVERSION\config? NOTE: This includes your startup.blend, bookmarks and any other file and directory you may have created in that directory" IDNO NextNoConfigRemove - RMDir /r "$BLENDERHOME\$SHORTVERSION\config" -NextNoConfigRemove: - MessageBox MB_YESNO "Recursively erase contents from $BLENDERHOME\$SHORTVERSION\plugins? NOTE: This includes files and subdirectories in this directory" IDNO NextNoPluginRemove - RMDir /r "$BLENDERHOME\$SHORTVERSION\plugins" -NextNoPluginRemove: - ; Try to remove dirs, but leave them if they contain anything - RMDir "$BLENDERHOME\$SHORTVERSION\plugins" - RMDir "$BLENDERHOME\$SHORTVERSION\config" - RMDir "$BLENDERHOME\$SHORTVERSION\scripts" - RMDir "$BLENDERHOME\$SHORTVERSION" - RMDir "$BLENDERHOME" + ${If} $REMOVECONFIG == "1" + RMDir /r "$BLENDERCONFIG\$SHORTVERSION" + ${Endif} + ; Remove shortcuts Delete "$SMPROGRAMS\Blender Foundation\Blender\*.*" Delete "$DESKTOP\Blender.lnk" |