bugfix [#23194] export UVs miss the extension file
also made all other exporters do this. Made some internal changes. - moved path functions from bpy.utils to bpy.path (similar to os.path) - added functions... bpy.path.ensure_ext(path, ".ext", case_sensitive=False) # simple function to ensure the extension is set. bpy.path.resolve_ncase(path) # useful for importing scenes made on windows where the path case doesnt match the files.
This commit is contained in:
@@ -922,7 +922,7 @@ def make_kf_obj_node(obj, name_to_id):
|
||||
"""
|
||||
|
||||
# import BPyMessages
|
||||
def save_3ds(filename, context):
|
||||
def write(filename, context):
|
||||
'''Save the Blender scene to a 3ds file.'''
|
||||
# Time the export
|
||||
|
||||
@@ -1107,12 +1107,7 @@ def save_3ds(filename, context):
|
||||
#primary.dump()
|
||||
|
||||
|
||||
# if __name__=='__main__':
|
||||
# if struct:
|
||||
# Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds'))
|
||||
# else:
|
||||
# Blender.Draw.PupMenu("Error%t|This script requires a full python installation")
|
||||
# # save_3ds('/test_b.3ds')
|
||||
# # write('/test_b.3ds')
|
||||
from bpy.props import *
|
||||
class Export3DS(bpy.types.Operator):
|
||||
'''Export to 3DS file format (.3ds)'''
|
||||
@@ -1127,7 +1122,10 @@ class Export3DS(bpy.types.Operator):
|
||||
check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
|
||||
|
||||
def execute(self, context):
|
||||
save_3ds(self.properties.filepath, context)
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, ".3ds")
|
||||
|
||||
write(filepath, context)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
@@ -75,7 +75,7 @@ def copy_images(dest_dir, textures):
|
||||
|
||||
image_paths = set()
|
||||
for tex in textures:
|
||||
image_paths.add(bpy.utils.expandpath(tex.filepath))
|
||||
image_paths.add(bpy.path.abspath(tex.filepath))
|
||||
|
||||
# Now copy images
|
||||
copyCount = 0
|
||||
@@ -176,7 +176,7 @@ def sane_name(data, dct):
|
||||
name = 'unnamed' # blank string, ASKING FOR TROUBLE!
|
||||
else:
|
||||
|
||||
name = bpy.utils.clean_name(name) # use our own
|
||||
name = bpy.path.clean_name(name) # use our own
|
||||
|
||||
while name in iter(dct.values()): name = increment_string(name)
|
||||
|
||||
@@ -200,14 +200,14 @@ def sane_groupname(data): return sane_name(data, sane_name_mapping_group)
|
||||
# FORCE_CWD - dont use the basepath, just add a ./ to the filename.
|
||||
# use when we know the file will be in the basepath.
|
||||
# '''
|
||||
# fname = bpy.utils.expandpath(fname_orig)
|
||||
# fname = bpy.path.abspath(fname_orig)
|
||||
# # fname = Blender.sys.expandpath(fname_orig)
|
||||
# fname_strip = os.path.basename(fname)
|
||||
# # fname_strip = strip_path(fname)
|
||||
# if FORCE_CWD:
|
||||
# fname_rel = '.' + os.sep + fname_strip
|
||||
# else:
|
||||
# fname_rel = bpy.utils.relpath(fname, basepath)
|
||||
# fname_rel = bpy.path.relpath(fname, basepath)
|
||||
# # fname_rel = Blender.sys.relpath(fname, basepath)
|
||||
# if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:]
|
||||
# return fname, fname_strip, fname_rel
|
||||
@@ -354,8 +354,8 @@ def write(filename, batch_objects = None, \
|
||||
|
||||
new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
|
||||
for data in data_seq: # scene or group
|
||||
newname = BATCH_FILE_PREFIX + bpy.utils.clean_name(data.name)
|
||||
# newname = BATCH_FILE_PREFIX + BPySys.bpy.utils.clean_name(data.name)
|
||||
newname = BATCH_FILE_PREFIX + bpy.path.clean_name(data.name)
|
||||
# newname = BATCH_FILE_PREFIX + BPySys.bpy.path.clean_name(data.name)
|
||||
|
||||
|
||||
if BATCH_OWN_DIR:
|
||||
@@ -1250,7 +1250,7 @@ def write(filename, batch_objects = None, \
|
||||
file.write('\n\t}')
|
||||
|
||||
def copy_image(image):
|
||||
fn = bpy.utils.expandpath(image.filepath)
|
||||
fn = bpy.path.abspath(image.filepath)
|
||||
fn_strip = os.path.basename(fn)
|
||||
|
||||
if EXP_IMAGE_COPY:
|
||||
@@ -3369,13 +3369,16 @@ class ExportFBX(bpy.types.Operator):
|
||||
if not self.properties.filepath:
|
||||
raise Exception("filepath not set")
|
||||
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, ".fbx")
|
||||
|
||||
GLOBAL_MATRIX = mtx4_identity
|
||||
GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self.properties.TX_SCALE
|
||||
if self.properties.TX_XROT90: GLOBAL_MATRIX = mtx4_x90n * GLOBAL_MATRIX
|
||||
if self.properties.TX_YROT90: GLOBAL_MATRIX = mtx4_y90n * GLOBAL_MATRIX
|
||||
if self.properties.TX_ZROT90: GLOBAL_MATRIX = mtx4_z90n * GLOBAL_MATRIX
|
||||
|
||||
write(self.properties.filepath,
|
||||
write(filepath,
|
||||
None, # XXX
|
||||
context,
|
||||
self.properties.EXP_OBS_SELECTED,
|
||||
@@ -3395,7 +3398,8 @@ class ExportFBX(bpy.types.Operator):
|
||||
self.properties.BATCH_ENABLE,
|
||||
self.properties.BATCH_GROUP,
|
||||
self.properties.BATCH_FILE_PREFIX,
|
||||
self.properties.BATCH_OWN_DIR)
|
||||
self.properties.BATCH_OWN_DIR,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -3413,7 +3417,7 @@ class ExportFBX(bpy.types.Operator):
|
||||
|
||||
# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
|
||||
# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print
|
||||
# - get rid of bpy.utils.clean_name somehow
|
||||
# - get rid of bpy.path.clean_name somehow
|
||||
# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565
|
||||
# + get rid of BPyObject_getObjectArmature, move it in RNA?
|
||||
# - BATCH_ENABLE and BATCH_GROUP options: line 327
|
||||
@@ -3428,7 +3432,7 @@ class ExportFBX(bpy.types.Operator):
|
||||
# - bpy.data.remove_scene: line 366
|
||||
# - bpy.sys.time move to bpy.sys.util?
|
||||
# - new scene creation, activation: lines 327-342, 368
|
||||
# - uses bpy.utils.expandpath, *.relpath - replace at least relpath
|
||||
# - uses bpy.path.abspath, *.relpath - replace at least relpath
|
||||
|
||||
# SMALL or COSMETICAL
|
||||
# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version')
|
||||
|
||||
@@ -171,10 +171,17 @@ class ExportMDD(bpy.types.Operator):
|
||||
return (ob and ob.type == 'MESH')
|
||||
|
||||
def execute(self, context):
|
||||
if not self.properties.filepath:
|
||||
raise Exception("filename not set")
|
||||
write(self.properties.filepath, context.scene, context.active_object,
|
||||
self.properties.frame_start, self.properties.frame_end, self.properties.fps)
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, ".mdd")
|
||||
|
||||
write(filepath,
|
||||
context.scene,
|
||||
context.active_object,
|
||||
self.properties.frame_start,
|
||||
self.properties.frame_end,
|
||||
self.properties.fps,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
@@ -66,7 +66,7 @@ def write_mtl(scene, filepath, copy_images, mtl_dict):
|
||||
dest_dir = os.path.dirname(filepath)
|
||||
|
||||
def copy_image(image):
|
||||
fn = bpy.utils.expandpath(image.filepath)
|
||||
fn = bpy.path.abspath(image.filepath)
|
||||
fn_strip = os.path.basename(fn)
|
||||
if copy_images:
|
||||
rel = fn_strip
|
||||
@@ -182,7 +182,7 @@ def copy_images(dest_dir):
|
||||
copyCount = 0
|
||||
|
||||
# for bImage in uniqueImages.values():
|
||||
# image_path = bpy.utils.expandpath(bImage.filepath)
|
||||
# image_path = bpy.path.abspath(bImage.filepath)
|
||||
# if bpy.sys.exists(image_path):
|
||||
# # Make a name for the target path.
|
||||
# dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
|
||||
@@ -790,7 +790,7 @@ def write(filepath, objects, scene,
|
||||
print("OBJ Export time: %.2f" % (time.clock() - time1))
|
||||
# print "OBJ Export time: %.2f" % (sys.time() - time1)
|
||||
|
||||
def do_export(filepath, context,
|
||||
def write(filepath, context,
|
||||
EXPORT_APPLY_MODIFIERS = True, # not used
|
||||
EXPORT_ROTX90 = True, # wrong
|
||||
EXPORT_TRI = False, # ok
|
||||
@@ -837,7 +837,7 @@ def do_export(filepath, context,
|
||||
orig_frame = scn.frame_current
|
||||
|
||||
if EXPORT_ALL_SCENES: # Add scene name into the context_name
|
||||
context_name[1] = '_%s' % bpy.utils.clean_name(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
|
||||
context_name[1] = '_%s' % bpy.path.clean_name(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
|
||||
|
||||
# Export an animation?
|
||||
if EXPORT_ANIMATION:
|
||||
@@ -927,27 +927,27 @@ class ExportOBJ(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
filepath = self.properties.filepath
|
||||
if not filepath.lower().endswith(".obj"):
|
||||
filepath += ".obj"
|
||||
filepath = bpy.path.ensure_ext(filepath, ".obj")
|
||||
|
||||
do_export(filepath, context,
|
||||
EXPORT_TRI=self.properties.use_triangles,
|
||||
EXPORT_EDGES=self.properties.use_edges,
|
||||
EXPORT_NORMALS=self.properties.use_normals,
|
||||
EXPORT_NORMALS_HQ=self.properties.use_hq_normals,
|
||||
EXPORT_UV=self.properties.use_uvs,
|
||||
EXPORT_MTL=self.properties.use_materials,
|
||||
EXPORT_COPY_IMAGES=self.properties.copy_images,
|
||||
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
|
||||
EXPORT_ROTX90=self.properties.use_rotate90,
|
||||
EXPORT_BLEN_OBS=self.properties.use_blen_objects,
|
||||
EXPORT_GROUP_BY_OB=self.properties.group_by_object,
|
||||
EXPORT_GROUP_BY_MAT=self.properties.group_by_material,
|
||||
EXPORT_KEEP_VERT_ORDER=self.properties.keep_vertex_order,
|
||||
EXPORT_POLYGROUPS=self.properties.use_vertex_groups,
|
||||
EXPORT_CURVE_AS_NURBS=self.properties.use_nurbs,
|
||||
EXPORT_SEL_ONLY=self.properties.use_selection,
|
||||
EXPORT_ALL_SCENES=self.properties.use_all_scenes)
|
||||
write(filepath, context,
|
||||
EXPORT_TRI=self.properties.use_triangles,
|
||||
EXPORT_EDGES=self.properties.use_edges,
|
||||
EXPORT_NORMALS=self.properties.use_normals,
|
||||
EXPORT_NORMALS_HQ=self.properties.use_hq_normals,
|
||||
EXPORT_UV=self.properties.use_uvs,
|
||||
EXPORT_MTL=self.properties.use_materials,
|
||||
EXPORT_COPY_IMAGES=self.properties.copy_images,
|
||||
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
|
||||
EXPORT_ROTX90=self.properties.use_rotate90,
|
||||
EXPORT_BLEN_OBS=self.properties.use_blen_objects,
|
||||
EXPORT_GROUP_BY_OB=self.properties.group_by_object,
|
||||
EXPORT_GROUP_BY_MAT=self.properties.group_by_material,
|
||||
EXPORT_KEEP_VERT_ORDER=self.properties.keep_vertex_order,
|
||||
EXPORT_POLYGROUPS=self.properties.use_vertex_groups,
|
||||
EXPORT_CURVE_AS_NURBS=self.properties.use_nurbs,
|
||||
EXPORT_SEL_ONLY=self.properties.use_selection,
|
||||
EXPORT_ALL_SCENES=self.properties.use_all_scenes,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -280,12 +280,10 @@ class ExportPLY(bpy.types.Operator):
|
||||
return context.active_object != None
|
||||
|
||||
def execute(self, context):
|
||||
# print("Selected: " + context.active_object.name)
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, ".ply")
|
||||
|
||||
if not self.properties.filepath:
|
||||
raise Exception("filename not set")
|
||||
|
||||
write(self.properties.filepath, context.scene, context.active_object,\
|
||||
write(filepath, context.scene, context.active_object,\
|
||||
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
|
||||
EXPORT_NORMALS=self.properties.use_normals,
|
||||
EXPORT_UV=self.properties.use_uvs,
|
||||
|
||||
@@ -794,7 +794,7 @@ class x3d_class:
|
||||
pic = tex.image
|
||||
|
||||
# using .expandpath just in case, os.path may not expect //
|
||||
basename = os.path.basename(bpy.utils.expandpath(pic.filepath))
|
||||
basename = os.path.basename(bpy.path.abspath(pic.filepath))
|
||||
|
||||
pic = alltextures[i].image
|
||||
# pic = alltextures[i].getImage()
|
||||
@@ -1140,7 +1140,7 @@ class x3d_class:
|
||||
# Callbacks, needed before Main
|
||||
##########################################################
|
||||
|
||||
def x3d_export(filename,
|
||||
def write(filename,
|
||||
context,
|
||||
EXPORT_APPLY_MODIFIERS=False,
|
||||
EXPORT_TRI=False,
|
||||
@@ -1175,47 +1175,6 @@ def x3d_export(filename,
|
||||
)
|
||||
|
||||
|
||||
def x3d_export_ui(filename):
|
||||
if not filename.endswith(extension):
|
||||
filename += extension
|
||||
#if _safeOverwrite and sys.exists(filename):
|
||||
# result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
|
||||
#if(result != 1):
|
||||
# return
|
||||
|
||||
# Get user options
|
||||
EXPORT_APPLY_MODIFIERS = Draw.Create(1)
|
||||
EXPORT_TRI = Draw.Create(0)
|
||||
EXPORT_GZIP = Draw.Create( filename.lower().endswith('.x3dz') )
|
||||
|
||||
# Get USER Options
|
||||
pup_block = [\
|
||||
('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object.'),\
|
||||
('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\
|
||||
('Compress', EXPORT_GZIP, 'GZip the resulting file, requires a full python install'),\
|
||||
]
|
||||
|
||||
if not Draw.PupBlock('Export...', pup_block):
|
||||
return
|
||||
|
||||
Blender.Window.EditMode(0)
|
||||
Blender.Window.WaitCursor(1)
|
||||
|
||||
x3d_export(filename,\
|
||||
EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val,\
|
||||
EXPORT_TRI = EXPORT_TRI.val,\
|
||||
EXPORT_GZIP = EXPORT_GZIP.val\
|
||||
)
|
||||
|
||||
Blender.Window.WaitCursor(0)
|
||||
|
||||
|
||||
|
||||
#########################################################
|
||||
# main routine
|
||||
#########################################################
|
||||
|
||||
|
||||
from bpy.props import *
|
||||
|
||||
class ExportX3D(bpy.types.Operator):
|
||||
@@ -1233,7 +1192,16 @@ class ExportX3D(bpy.types.Operator):
|
||||
compress = BoolProperty(name="Compress", description="GZip the resulting file, requires a full python install", default=False)
|
||||
|
||||
def execute(self, context):
|
||||
x3d_export(self.properties.filepath, context, self.properties.apply_modifiers, self.properties.triangulate, self.properties.compress)
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, ".x3d")
|
||||
|
||||
write(filepath,
|
||||
context,
|
||||
self.properties.apply_modifiers,
|
||||
self.properties.triangulate,
|
||||
self.properties.compress,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
@@ -49,7 +49,7 @@ def addPointCache(job, ob, point_cache, default_path):
|
||||
if name == "":
|
||||
name = "".join(["%02X" % ord(c) for c in ob.name])
|
||||
|
||||
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
|
||||
cache_path = bpy.path.abspath(point_cache.filepath) if point_cache.external else default_path
|
||||
|
||||
index = "%02i" % point_cache.index
|
||||
|
||||
@@ -113,7 +113,7 @@ def clientSendJob(conn, scene, anim = False):
|
||||
# LIBRARIES
|
||||
###########################
|
||||
for lib in bpy.data.libraries:
|
||||
file_path = bpy.utils.expandpath(lib.filepath)
|
||||
file_path = bpy.path.abspath(lib.filepath)
|
||||
if os.path.exists(file_path):
|
||||
job.addFile(file_path)
|
||||
|
||||
@@ -122,7 +122,7 @@ def clientSendJob(conn, scene, anim = False):
|
||||
###########################
|
||||
for image in bpy.data.images:
|
||||
if image.source == "FILE" and not image.packed_file:
|
||||
file_path = bpy.utils.expandpath(image.filepath)
|
||||
file_path = bpy.path.abspath(image.filepath)
|
||||
if os.path.exists(file_path):
|
||||
job.addFile(file_path)
|
||||
|
||||
@@ -139,7 +139,7 @@ def clientSendJob(conn, scene, anim = False):
|
||||
for object in bpy.data.objects:
|
||||
for modifier in object.modifiers:
|
||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
|
||||
addFluidFiles(job, bpy.path.abspath(modifier.settings.path))
|
||||
elif modifier.type == "CLOTH":
|
||||
addPointCache(job, object, modifier.point_cache, default_path)
|
||||
elif modifier.type == "SOFT_BODY":
|
||||
@@ -149,7 +149,7 @@ def clientSendJob(conn, scene, anim = False):
|
||||
if modifier.domain_settings.highres:
|
||||
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
|
||||
elif modifier.type == "MULTIRES" and modifier.external:
|
||||
file_path = bpy.utils.expandpath(modifier.filepath)
|
||||
file_path = bpy.path.abspath(modifier.filepath)
|
||||
job.addFile(file_path)
|
||||
|
||||
# particles modifier are stupid and don't contain data
|
||||
|
||||
@@ -92,7 +92,7 @@ def process(paths):
|
||||
# LIBRARIES
|
||||
###########################
|
||||
for lib in bpy.data.libraries:
|
||||
file_path = bpy.utils.expandpath(lib.filepath)
|
||||
file_path = bpy.path.abspath(lib.filepath)
|
||||
new_path = path_map.get(os.path.split(file_path)[1], None)
|
||||
if new_path:
|
||||
lib.filepath = new_path
|
||||
@@ -102,7 +102,7 @@ def process(paths):
|
||||
###########################
|
||||
for image in bpy.data.images:
|
||||
if image.source == "FILE" and not image.packed_file:
|
||||
file_path = bpy.utils.expandpath(image.filepath)
|
||||
file_path = bpy.path.abspath(image.filepath)
|
||||
new_path = path_map.get(os.path.split(file_path)[1], None)
|
||||
if new_path:
|
||||
image.filepath = new_path
|
||||
@@ -124,7 +124,7 @@ def process(paths):
|
||||
if modifier.domain_settings.highres:
|
||||
processPointCache(modifier.domain_settings.point_cache_high)
|
||||
elif modifier.type == "MULTIRES" and modifier.external:
|
||||
file_path = bpy.utils.expandpath(modifier.filepath)
|
||||
file_path = bpy.path.abspath(modifier.filepath)
|
||||
new_path = path_map.get(file_path, None)
|
||||
if new_path:
|
||||
modifier.filepath = new_path
|
||||
|
||||
@@ -26,7 +26,7 @@ data = _bpy.data
|
||||
context = _bpy.context
|
||||
|
||||
# python modules
|
||||
from bpy import utils
|
||||
from bpy import utils, path
|
||||
|
||||
from bpy import ops as _ops_module
|
||||
|
||||
|
||||
173
release/scripts/modules/bpy/path.py
Normal file
173
release/scripts/modules/bpy/path.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
"""
|
||||
This module has a similar scope to os.path, containing utility
|
||||
functions for dealing with paths in Blender.
|
||||
"""
|
||||
|
||||
import bpy as _bpy
|
||||
|
||||
|
||||
def expand(path):
|
||||
"""
|
||||
Returns the absolute path relative to the current blend file using the "//" prefix.
|
||||
"""
|
||||
if path.startswith("//"):
|
||||
return _os.path.join(_os.path.dirname(_bpy.data.filepath), path[2:])
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def relpath(path, start=None):
|
||||
"""
|
||||
Returns the path relative to the current blend file using the "//" prefix.
|
||||
|
||||
:arg start: Relative to this path, when not set the current filename is used.
|
||||
:type start: string
|
||||
"""
|
||||
if not path.startswith("//"):
|
||||
if start is None:
|
||||
start = _os.path.dirname(_bpy.data.filepath)
|
||||
return "//" + _os.path.relpath(path, start)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def clean_name(name, replace="_"):
|
||||
"""
|
||||
Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
|
||||
All characters besides A-Z/a-z, 0-9 are replaced with "_"
|
||||
or the replace argument if defined.
|
||||
"""
|
||||
|
||||
unclean_chars = \
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
|
||||
\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\
|
||||
\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
|
||||
\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b\
|
||||
\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\
|
||||
\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\
|
||||
\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\
|
||||
\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\
|
||||
\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\
|
||||
\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\
|
||||
\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\
|
||||
\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\
|
||||
\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
|
||||
|
||||
for ch in unclean_chars:
|
||||
name = name.replace(ch, replace)
|
||||
return name
|
||||
|
||||
|
||||
def display_name(name):
|
||||
"""
|
||||
Creates a display string from name to be used menus and the user interface.
|
||||
Capitalize the first letter in all lowercase names, mixed case names are kept as is.
|
||||
Intended for use with filenames and module names.
|
||||
"""
|
||||
name_base = _os.path.splitext(name)[0]
|
||||
|
||||
# string replacements
|
||||
name_base = name_base.replace("_colon_", ":")
|
||||
|
||||
name_base = name_base.replace("_", " ")
|
||||
|
||||
if name_base.islower():
|
||||
return name_base.capitalize()
|
||||
else:
|
||||
return name_base
|
||||
|
||||
|
||||
def resolve_ncase(path):
|
||||
"""
|
||||
Resolve a case insensitive path on a case sensitive system,
|
||||
returning a string with the path if found else return the original path.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
def _ncase_path_found(path):
|
||||
if path=='' or os.path.exists(path):
|
||||
return path, True
|
||||
|
||||
filename = os.path.basename(path) # filename may be a directory or a file
|
||||
dirpath = os.path.dirname(path)
|
||||
|
||||
suffix = ""
|
||||
if not filename: # dir ends with a slash?
|
||||
if len(dirpath) < len(path):
|
||||
suffix = path[:len(path)-len(dirpath)]
|
||||
|
||||
filename = os.path.basename(dirpath)
|
||||
dirpath = os.path.dirname(dirpath)
|
||||
|
||||
if not os.path.exists(dirpath):
|
||||
dirpath, found = _ncase_path_found(dirpath)
|
||||
|
||||
if not found:
|
||||
return path, False
|
||||
|
||||
# at this point, the directory exists but not the file
|
||||
|
||||
# we are expecting 'dirpath' to be a directory, but it could be a file
|
||||
if os.path.isdir(dirpath):
|
||||
files = os.listdir(dirpath)
|
||||
else:
|
||||
return path, False
|
||||
|
||||
filename_low = filename.lower()
|
||||
f_iter_nocase = None
|
||||
|
||||
for f_iter in files:
|
||||
if f_iter.lower() == filename_low:
|
||||
f_iter_nocase = f_iter
|
||||
break
|
||||
|
||||
if f_iter_nocase:
|
||||
return os.path.join(dirpath, f_iter_nocase) + suffix, True
|
||||
else:
|
||||
# cant find the right one, just return the path as is.
|
||||
return path, False
|
||||
|
||||
ncase_path, found = _ncase_path_found(path)
|
||||
return ncase_path if found else path
|
||||
|
||||
|
||||
def ensure_ext(filepath, ext, case_sensitive=False):
|
||||
"""
|
||||
Return the path with the extension added its its not alredy set.
|
||||
|
||||
:arg ext: The extension to check for.
|
||||
:type ext: string
|
||||
:arg case_sensitive: Check for matching case when comparing extensions.
|
||||
:type case_sensitive: bool
|
||||
"""
|
||||
import os
|
||||
fn_base, fn_ext = os.path.splitext(filepath)
|
||||
if fn_base and fn_ext:
|
||||
if (case_sensitive and ext == fn_ext) or (ext.lower() == fn_ext.lower()):
|
||||
return filepath
|
||||
else:
|
||||
return fn_base + ext
|
||||
|
||||
else:
|
||||
return filepath + ext
|
||||
@@ -216,75 +216,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
|
||||
_bpy_types._register_immediate = True
|
||||
|
||||
|
||||
def expandpath(path):
|
||||
"""
|
||||
Returns the absolute path relative to the current blend file using the "//" prefix.
|
||||
"""
|
||||
if path.startswith("//"):
|
||||
return _os.path.join(_os.path.dirname(_bpy.data.filepath), path[2:])
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def relpath(path, start=None):
|
||||
"""
|
||||
Returns the path relative to the current blend file using the "//" prefix.
|
||||
|
||||
:arg start: Relative to this path, when not set the current filename is used.
|
||||
:type start: string
|
||||
"""
|
||||
if not path.startswith("//"):
|
||||
if start is None:
|
||||
start = _os.path.dirname(_bpy.data.filepath)
|
||||
return "//" + _os.path.relpath(path, start)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def clean_name(name, replace="_"):
|
||||
"""
|
||||
Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
|
||||
All characters besides A-Z/a-z, 0-9 are replaced with "_"
|
||||
or the replace argument if defined.
|
||||
"""
|
||||
|
||||
unclean_chars = \
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
|
||||
\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\
|
||||
\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
|
||||
\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b\
|
||||
\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\
|
||||
\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\
|
||||
\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\
|
||||
\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\
|
||||
\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\
|
||||
\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\
|
||||
\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\
|
||||
\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\
|
||||
\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
|
||||
|
||||
for ch in unclean_chars:
|
||||
name = name.replace(ch, replace)
|
||||
return name
|
||||
|
||||
|
||||
def display_name(name):
|
||||
"""
|
||||
Creates a display string from name to be used menus and the user interface.
|
||||
Capitalize the first letter in all lowercase names, mixed case names are kept as is.
|
||||
Intended for use with filenames and module names.
|
||||
"""
|
||||
name_base = _os.path.splitext(name)[0]
|
||||
|
||||
# string replacements
|
||||
name_base = name_base.replace("_colon_", ":")
|
||||
|
||||
name_base = name_base.replace("_", " ")
|
||||
|
||||
if name_base.islower():
|
||||
return name_base.capitalize()
|
||||
else:
|
||||
return name_base
|
||||
|
||||
|
||||
# base scripts
|
||||
|
||||
@@ -718,7 +718,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
if f.startswith("."):
|
||||
continue
|
||||
|
||||
preset_name = bpy.utils.display_name(f)
|
||||
preset_name = bpy.path.display_name(f)
|
||||
props = layout.operator(operator, text=preset_name)
|
||||
|
||||
for attr, value in props_default.items():
|
||||
|
||||
@@ -537,7 +537,7 @@ def generate_test_all(context, GRAPH=False):
|
||||
base_name = os.path.splitext(bpy.data.filepath)[0]
|
||||
for obj, obj_new in new_objects:
|
||||
for obj in (obj, obj_new):
|
||||
fn = base_name + "-" + bpy.utils.clean_name(obj.name)
|
||||
fn = base_name + "-" + bpy.path.clean_name(obj.name)
|
||||
|
||||
path_dot = fn + ".dot"
|
||||
path_png = fn + ".png"
|
||||
|
||||
@@ -58,7 +58,7 @@ class EditExternally(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
import os
|
||||
import subprocess
|
||||
filepath = bpy.utils.expandpath(self.properties.filepath)
|
||||
filepath = bpy.path.abspath(self.properties.filepath)
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
self.report('ERROR', "Image path '%s' not found." % filepath)
|
||||
@@ -93,7 +93,7 @@ class SaveDirty(bpy.types.Operator):
|
||||
unique_paths = set()
|
||||
for image in bpy.data.images:
|
||||
if image.dirty:
|
||||
filepath = bpy.utils.expandpath(image.filepath)
|
||||
filepath = bpy.path.abspath(image.filepath)
|
||||
if "\\" not in filepath and "/" not in filepath:
|
||||
self.report({'WARNING'}, "Invalid path: " + filepath)
|
||||
elif filepath in unique_paths:
|
||||
@@ -135,7 +135,7 @@ class ProjectEdit(bpy.types.Operator):
|
||||
|
||||
filepath = os.path.basename(bpy.data.filepath)
|
||||
filepath = os.path.splitext(filepath)[0]
|
||||
# filepath = bpy.utils.clean_name(filepath) # fixes <memory> rubbish, needs checking
|
||||
# filepath = bpy.path.clean_name(filepath) # fixes <memory> rubbish, needs checking
|
||||
|
||||
if filepath.startswith(".") or filepath == "":
|
||||
# TODO, have a way to check if the file is saved, assume .B25.blend
|
||||
@@ -147,12 +147,12 @@ class ProjectEdit(bpy.types.Operator):
|
||||
obj = context.object
|
||||
|
||||
if obj:
|
||||
filepath += "_" + bpy.utils.clean_name(obj.name)
|
||||
filepath += "_" + bpy.path.clean_name(obj.name)
|
||||
|
||||
filepath_final = filepath + "." + EXT
|
||||
i = 0
|
||||
|
||||
while os.path.exists(bpy.utils.expandpath(filepath_final)):
|
||||
while os.path.exists(bpy.path.abspath(filepath_final)):
|
||||
filepath_final = filepath + ("%.3d.%s" % (i, EXT))
|
||||
i += 1
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class PlayRenderedAnim(bpy.types.Operator):
|
||||
|
||||
preset = prefs.filepaths.animation_player_preset
|
||||
player_path = prefs.filepaths.animation_player
|
||||
file_path = bpy.utils.expandpath(rd.output_path)
|
||||
file_path = bpy.path.abspath(rd.output_path)
|
||||
is_movie = rd.is_movie_format
|
||||
|
||||
# try and guess a command line if it doesn't exist
|
||||
@@ -105,7 +105,7 @@ class PlayRenderedAnim(bpy.types.Operator):
|
||||
# works for movies and images
|
||||
file = rd.frame_path(frame=scene.frame_start)
|
||||
|
||||
file = bpy.utils.expandpath(file) # expand '//'
|
||||
file = bpy.path.abspath(file) # expand '//'
|
||||
|
||||
cmd = [player_path]
|
||||
# extra options, fps controls etc.
|
||||
|
||||
@@ -114,7 +114,9 @@ class ExportUVLayout(bpy.types.Operator):
|
||||
|
||||
mode = self.properties.mode
|
||||
|
||||
file = open(self.properties.filepath, "w")
|
||||
filepath = self.properties.filepath
|
||||
filepath = bpy.path.ensure_ext(filepath, "." + mode.lower())
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
if mode == 'SVG':
|
||||
|
||||
@@ -478,7 +478,7 @@ class WM_OT_path_open(bpy.types.Operator):
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
filepath = bpy.utils.expandpath(self.properties.filepath)
|
||||
filepath = bpy.path.abspath(self.properties.filepath)
|
||||
filepath = os.path.normpath(filepath)
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
|
||||
@@ -213,7 +213,7 @@ class Graph(bpy.types.Operator):
|
||||
import bpy
|
||||
reload(graphviz_export)
|
||||
obj = bpy.context.object
|
||||
path = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name)
|
||||
path = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.path.clean_name(obj.name)
|
||||
path_dot = path + ".dot"
|
||||
path_png = path + ".png"
|
||||
saved = graphviz_export.graph_armature(bpy.context.object, path_dot, CONSTRAINTS=False, DRIVERS=False)
|
||||
@@ -250,7 +250,7 @@ class AsScript(bpy.types.Operator):
|
||||
def invoke(self, context, event):
|
||||
import os
|
||||
obj = context.object
|
||||
self.properties.filepath = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name) + ".py"
|
||||
self.properties.filepath = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.path.clean_name(obj.name) + ".py"
|
||||
wm = context.manager
|
||||
wm.add_fileselect(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
@@ -307,7 +307,7 @@ class INFO_MT_armature_metarig_add(bpy.types.Menu):
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
|
||||
for submodule_type in rigify.get_submodule_types():
|
||||
text = bpy.utils.display_name(submodule_type)
|
||||
text = bpy.path.display_name(submodule_type)
|
||||
layout.operator("pose.metarig_sample_add", text=text, icon='OUTLINER_OB_ARMATURE').metarig_type = submodule_type
|
||||
|
||||
menu_func = (lambda self, context: self.layout.menu("INFO_MT_armature_metarig_add", icon='OUTLINER_OB_ARMATURE'))
|
||||
|
||||
@@ -360,6 +360,7 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
# py modules
|
||||
fw(" bpy.utils.rst\n\n")
|
||||
fw(" bpy.path.rst\n\n")
|
||||
fw(" bpy.app.rst\n\n")
|
||||
|
||||
# C modules
|
||||
@@ -440,6 +441,9 @@ def rna2sphinx(BASEPATH):
|
||||
from bpy import utils as module
|
||||
pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)")
|
||||
|
||||
from bpy import path as module
|
||||
pymodule2sphinx(BASEPATH, "bpy.path", module, "Path Utilities (bpy.path)")
|
||||
|
||||
# C modules
|
||||
from bpy import app as module
|
||||
pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data (bpy.app)")
|
||||
|
||||
Reference in New Issue
Block a user