[Bf-extensions-cvs] [97812d7] master: import images as planes rewrite: D2239
meta-androcto
noreply at git.blender.org
Sun Jan 15 05:33:24 CET 2017
Commit: 97812d737e446da73f24754340cc9b7e6003c774
Author: meta-androcto
Date: Sun Jan 15 15:32:56 2017 +1100
Branches: master
https://developer.blender.org/rBA97812d737e446da73f24754340cc9b7e6003c774
import images as planes rewrite: D2239
===================================================================
M io_import_images_as_planes.py
===================================================================
diff --git a/io_import_images_as_planes.py b/io_import_images_as_planes.py
index 7ba5bfa..26dcba3 100644
--- a/io_import_images_as_planes.py
+++ b/io_import_images_as_planes.py
@@ -16,11 +16,13 @@
#
# ##### END GPL LICENSE BLOCK #####
+# <pep8 compliant>
+
bl_info = {
"name": "Import Images as Planes",
- "author": "Florian Meyer (tstscr), mont29, matali",
- "version": (2, 0, 5),
- "blender": (2, 76, 1),
+ "author": "Florian Meyer (tstscr), mont29, matali, Ted Schundler (SpkyElctrc)",
+ "version": (3, 0, 0),
+ "blender": (2, 77, 0),
"location": "File > Import > Images as Planes or Add > Mesh > Images as Planes",
"description": "Imports images and creates planes with the appropriate aspect ratio. "
"The images are mapped to the planes.",
@@ -30,72 +32,262 @@ bl_info = {
"category": "Import-Export",
}
+import os
+import warnings
+import re
+from itertools import count, repeat
+from collections import namedtuple
+from math import pi
+
import bpy
from bpy.types import Operator
-import mathutils
-import os
-import collections
+from mathutils import Vector
from bpy.props import (
- StringProperty,
- BoolProperty,
- EnumProperty,
- IntProperty,
- FloatProperty,
- CollectionProperty,
- )
+ StringProperty,
+ BoolProperty,
+ EnumProperty,
+ FloatProperty,
+ CollectionProperty)
+
+from bpy_extras.object_utils import (
+ AddObjectHelper,
+ world_to_camera_view)
-from bpy_extras.object_utils import AddObjectHelper, object_data_add
from bpy_extras.image_utils import load_image
# -----------------------------------------------------------------------------
-# Global Vars
-
-DEFAULT_EXT = "*"
-
-EXT_FILTER = getattr(collections, "OrderedDict", dict)((
- (DEFAULT_EXT, ((), "All image formats", "Import all known image (or movie) formats.")),
- ("jpeg", (("jpeg", "jpg", "jpe"), "JPEG ({})", "Joint Photographic Experts Group")),
- ("png", (("png", ), "PNG ({})", "Portable Network Graphics")),
- ("tga", (("tga", "tpic"), "Truevision TGA ({})", "")),
- ("tiff", (("tiff", "tif"), "TIFF ({})", "Tagged Image File Format")),
- ("bmp", (("bmp", "dib"), "BMP ({})", "Windows Bitmap")),
- ("cin", (("cin", ), "CIN ({})", "")),
- ("dpx", (("dpx", ), "DPX ({})", "DPX (Digital Picture Exchange)")),
- ("psd", (("psd", ), "PSD ({})", "Photoshop Document")),
- ("exr", (("exr", ), "OpenEXR ({})", "OpenEXR HDR imaging image file format")),
- ("hdr", (("hdr", "pic"), "Radiance HDR ({})", "")),
- ("avi", (("avi", ), "AVI ({})", "Audio Video Interleave")),
- ("mov", (("mov", "qt"), "QuickTime ({})", "")),
- ("mp4", (("mp4", ), "MPEG-4 ({})", "MPEG-4 Part 14")),
- ("ogg", (("ogg", "ogv"), "OGG Theora ({})", "")),
-))
-
-# XXX Hack to avoid allowing videos with Cycles, crashes currently!
-VID_EXT_FILTER = {e for ext_k, ext_v in EXT_FILTER.items() if ext_k in {"avi", "mov", "mp4", "ogg"} for e in ext_v[0]}
-
-CYCLES_SHADERS = (
- ('BSDF_DIFFUSE', "Diffuse", "Diffuse Shader"),
- ('EMISSION', "Emission", "Emission Shader")
-)
+# Module-level Shared State
+
+watched_objects = {} # used to trigger compositor updates on scene updates
+
# -----------------------------------------------------------------------------
# Misc utils.
-def gen_ext_filter_ui_items():
- return tuple((k, name.format(", ".join("." + e for e in exts)) if "{}" in name else name, desc)
- for k, (exts, name, desc) in EXT_FILTER.items())
+def str_to_vector(s):
+ """Convert a string into a vector.
+
+ >>> str_to_vector("1,2,3")
+ Vector((1.0, 2.0, 3.0))
+
+ >>> str_to_vector("1.0, 0.0, 0, 1")
+ Vector((1.0, 0.0, 0.0, 1.0))
+ """
+ return Vector(map(float, s.split(',')))
+
+
+def add_driver_prop(driver, name, type, id, path):
+ """Configure a new driver variable."""
+ dv = driver.variables.new()
+ dv.name = name
+ dv.type = 'SINGLE_PROP'
+ target = dv.targets[0]
+ target.id_type = type
+ target.id = id
+ target.data_path = path
+
+
+def context_to_dict(context):
+ """Create a dictionary from the current context
+
+ >>> import bpy
+ >>> ctx = context_to_dict(bpy.context)
+ >>> assert ctx['window'] == bpy.context.window
+ """
+ return {
+ k: getattr(context, k) for k in
+ ('window', 'screen', 'area', 'scene', 'object', 'selected_objects')
+ }
+
+# -----------------------------------------------------------------------------
+# Image loading
+
+ImageSpec = namedtuple(
+ 'ImageSpec',
+ ['image', 'size', 'frame_start', 'frame_offset', 'frame_duration'])
+
+num_regex = re.compile('[0-9]') # Find a single number
+nums_regex = re.compile('[0-9]+') # Find a set of numbers
+
+
+def find_image_sequences(files):
+ """From a group of files, detect image sequences.
+
+ This returns a generator of tuples, which contain the filename,
+ start frame, and length of the detected sequence
+
+ >>> list(find_image_sequences([
+ ... "test2-001.jp2", "test2-002.jp2",
+ ... "test3-003.jp2", "test3-004.jp2", "test3-005.jp2", "test3-006.jp2",
+ ... "blaah"]))
+ [('blaah', 1, 1), ('test2-001.jp2', 1, 2), ('test3-003.jp2', 3, 4)]
+
+ """
+ files = iter(sorted(files))
+ prev_file = None
+ pattern = ""
+ matches = []
+ segment = None
+ length = 1
+ for filename in files:
+ new_pattern = num_regex.sub('#', filename)
+ new_matches = list(map(int, nums_regex.findall(filename)))
+ if new_pattern == pattern:
+ # this file looks like it may be in sequence from the previous
+
+ # if there are multiple sets of numbers, figure out what changed
+ if segment is None:
+ for i, prev, cur in zip(count(), matches, new_matches):
+ if prev != cur:
+ segment = i
+ break
+
+ # did it only change by one?
+ for i, prev, cur in zip(count(), matches, new_matches):
+ if i == segment:
+ # We expect this to increment
+ prev = prev + length
+ if prev != cur:
+ break
+
+ # All good!
+ else:
+ length += 1
+ continue
+
+ # No continuation -> spit out what we found and reset counters
+ if prev_file:
+ if length > 1:
+ yield prev_file, matches[segment], length
+ else:
+ yield prev_file, 1, 1
+
+ prev_file = filename
+ matches = new_matches
+ pattern = new_pattern
+ segment = None
+ length = 1
-def is_image_fn(fn, ext_key):
- if ext_key == DEFAULT_EXT:
- return True # Using Blender's image/movie filter.
- ext = os.path.splitext(fn)[1].lstrip(".").lower()
- return ext in EXT_FILTER[ext_key][0]
+ if prev_file:
+ if length > 1:
+ yield prev_file, matches[segment], length
+ else:
+ yield prev_file, 1, 1
+
+
+def load_images(filenames, directory, force_reload=False, frame_start=1, find_sequences=False):
+ """Wrapper for bpy's load_image
+
+ Loads a set of images, movies, or even image sequences
+ Returns a generator of ImageSpec wrapper objects later used for texture setup
+ """
+ if find_sequences: # if finding sequences, we need some pre-processing first
+ file_iter = find_image_sequences(filenames)
+ else:
+ file_iter = zip(filenames, repeat(1), repeat(1))
+
+ for filename, offset, frames in file_iter:
+ image = load_image(filename, directory, check_existing=True, force_reload=force_reload)
+
+ # Size is unavailable for sequences, so we grab it early
+ size = tuple(image.size)
+
+ if image.source == 'MOVIE':
+ # Blender BPY BUG!
+ # This number is only valid when read a second time in 2.77
+ # This repeated line is not a mistake
+ frames = image.frame_duration
+ frames = image.frame_duration
+
+ elif frames > 1: # Not movie, but multiple frames -> image sequence
+ image.use_animation = True
+ image.source = 'SEQUENCE'
+
+ yield ImageSpec(image, size, frame_start, offset - 1, frames)
# -----------------------------------------------------------------------------
-# Cycles utils.
-def get_input_nodes(node, nodes, links):
+# Position & Size Helpers
+
+def offset_planes(planes, gap, axis):
+ """Offset planes from each other by `gap` amount along a _local_ vector `axis`
+
+ For example, offset_planes([obj1, obj2], 0.5, Vector(0, 0, 1)) will place
+ obj2 0.5 blender units away from obj1 along the local positive Z axis.
+
+ This is in local space, not world space, so all planes should share
+ a common scale and rotation.
+ """
+ prior = planes[0]
+ offset = Vector()
+ for current in planes[1:]:
+
+ local_offset = abs((prior.dimensions + current.dimensions) * axis) / 2.0 + gap
+
+ offset += local_offset * axis
+ current.location = current.matrix_world * offset
+
+ prior = current
+
+
+def compute_camera_size(context, center, fill_mode, aspect):
+ """Determine how large an object needs to be to fit or fill the camera's field of view."""
+ scene = context.scene
+ camera = scene.camera
+ view_frame = camera.data.view_frame(scene=scene)
+ frame_size = \
+ Vector([max(v[i] for v in view_frame) for i in range(3)]) - \
+ Vector([min(v[i] for v in view_frame) for i in range(3)])
+ camera_aspect = frame_size.x / frame_size.y
+
+ # Convert the frame size to the correct sizing at a given distance
+ if camera.type == 'ORTHO':
+ frame_size = frame_size.xy
+ else:
+ # Perspective transform
+ distance = world_to_camera_view(scene, camera, center).z
+ frame_size = distance * frame_size.xy / (-view_frame[0].z)
+
+ # Determine what axis to match to the camera
+ match_axis = 0 # match the Y axis size
+ match_aspect = aspect
+ if (fill_mode == 'FILL' and aspect > camera_aspect) or \
+ (fill_mode == 'FIT' and
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list