Merge branch 'blender2.8' of git.blender.org:blender-addons into blender2.8

This commit is contained in:
Kalle-Samuli Riihikoski 2018-11-08 21:54:05 +02:00
commit 2bb579b798
73 changed files with 3469 additions and 2901 deletions

View File

@ -21,9 +21,9 @@
bl_info = {
"name": "IvyGen",
"author": "testscreenings, PKHG, TrumanBlending",
"version": (0, 1, 4),
"blender": (2, 59, 0),
"location": "View3D > Tool Shelf > Create > Ivy Generator",
"version": (0, 1, 5),
"blender": (2, 80, 0),
"location": "View3D > Properties Panel > Ivy Generator",
"description": "Adds generated ivy to a mesh object starting "
"at the 3D cursor",
"warning": "",
@ -35,29 +35,30 @@ bl_info = {
import bpy
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
Operator,
Panel,
PropertyGroup,
)
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
PointerProperty,
)
BoolProperty,
FloatProperty,
IntProperty,
PointerProperty,
)
from mathutils.bvhtree import BVHTree
from mathutils import (
Vector,
Matrix,
)
Vector,
Matrix,
)
from collections import deque
from math import (
pow, cos,
pi, atan2,
)
pow, cos,
pi, atan2,
)
from random import (
random as rand_val,
seed as rand_seed,
)
random as rand_val,
seed as rand_seed,
)
import time
@ -193,7 +194,7 @@ def createIvyGeometry(IVY, growLeaves):
# Add the object and link to scene
newCurve = bpy.data.objects.new("IVY_Curve", curve)
bpy.context.scene.objects.link(newCurve)
bpy.context.collection.objects.link(newCurve)
if growLeaves:
faceList = [[4 * i + l for l in range(4)] for i in
@ -204,9 +205,9 @@ def createIvyGeometry(IVY, growLeaves):
me.from_pydata(vertList, [], faceList)
me.update(calc_edges=True)
ob = bpy.data.objects.new('IvyLeaf', me)
bpy.context.scene.objects.link(ob)
bpy.context.collection.objects.link(ob)
me.uv_textures.new("Leaves")
me.uv_layers.new(name="Leaves")
# Set the uv texture coords
# TODO, this is non-functional, default uvs are ok?
@ -292,7 +293,7 @@ class Ivy:
tmpRoot.ivyNodes.append(tmpIvy)
self.ivyRoots.append(tmpRoot)
def grow(self, ob):
def grow(self, ob, bvhtree):
# Determine the local sizes
# local_ivySize = self.ivySize # * radius
# local_maxFloatLength = self.maxFloatLength # * radius
@ -319,8 +320,8 @@ class Ivy:
randomVector.normalize()
# Calculate the adhesion vector
adhesionVector = adhesion(prevIvy.pos, ob,
self.maxAdhesionDistance)
adhesionVector = adhesion(
prevIvy.pos, bvhtree, self.maxAdhesionDistance)
# Calculate the growing vector
growVector = self.ivySize * (primaryVector * self.primaryWeight +
@ -337,7 +338,7 @@ class Ivy:
newPos = prevIvy.pos + growVector + gravityVector
# Check for collisions with the object
climbing = collision(ob, prevIvy.pos, newPos)
climbing, newPos = collision(bvhtree, prevIvy.pos, newPos)
# Update the growing vector for any collisions
growVector = newPos - prevIvy.pos - gravityVector
@ -396,17 +397,13 @@ class Ivy:
return
def adhesion(loc, ob, max_l):
# Get transfor vector and transformed loc
tran_mat = ob.matrix_world.inverted()
tran_loc = tran_mat * loc
def adhesion(loc, bvhtree, max_l):
# Compute the adhesion vector by finding the nearest point
nearest_result = ob.closest_point_on_mesh(tran_loc, max_l)
nearest_location, *_ = bvhtree.find_nearest(loc, max_l)
adhesion_vector = Vector((0.0, 0.0, 0.0))
if nearest_result[0]:
if nearest_location is not None:
# Compute the distance to the nearest point
adhesion_vector = ob.matrix_world * nearest_result[1] - loc
adhesion_vector = nearest_location - loc
distance = adhesion_vector.length
# If it's less than the maximum allowed and not 0, continue
if distance:
@ -417,31 +414,37 @@ def adhesion(loc, ob, max_l):
return adhesion_vector
def collision(ob, pos, new_pos):
def collision(bvhtree, pos, new_pos):
# Check for collision with the object
climbing = False
# Transform vecs
tran_mat = ob.matrix_world.inverted()
tran_pos = tran_mat * pos
tran_new_pos = tran_mat * new_pos
tran_dir = tran_new_pos - tran_pos
corrected_new_pos = new_pos
direction = new_pos - pos
ray_result = ob.ray_cast(tran_pos, tran_dir, tran_dir.length)
hit_location, hit_normal, *_ = bvhtree.ray_cast(pos, direction, direction.length)
# If there's a collision we need to check it
if ray_result[0]:
if hit_location is not None:
# Check whether the collision is going into the object
if tran_dir.dot(ray_result[2]) < 0.0:
# Find projection of the point onto the plane
p0 = tran_new_pos - (tran_new_pos -
ray_result[1]).project(ray_result[2])
# Reflect in the plane
tran_new_pos += 2 * (p0 - tran_new_pos)
new_pos *= 0
new_pos += ob.matrix_world * tran_new_pos
climbing = True
return climbing
if direction.dot(hit_normal) < 0.0:
reflected_direction = (new_pos - hit_location).reflect(hit_normal)
corrected_new_pos = hit_location + reflected_direction
climbing = True
return climbing, corrected_new_pos
def bvhtree_from_object(ob):
import bmesh
bm = bmesh.new()
mesh = ob.to_mesh(bpy.context.depsgraph, True)
bm.from_mesh(mesh)
bm.transform(ob.matrix_world)
bvhtree = BVHTree.FromBMesh(bm)
bpy.data.meshes.remove(mesh)
return bvhtree
def check_mesh_faces(ob):
me = ob.data
@ -457,16 +460,16 @@ class IvyGen(Operator):
bl_description = "Generate Ivy on an Mesh Object"
bl_options = {'REGISTER', 'UNDO'}
updateIvy = BoolProperty(
name="Update Ivy",
description="Update the Ivy location based on the cursor and Panel settings",
default=False
)
defaultIvy = BoolProperty(
name="Default Ivy",
options={"HIDDEN", "SKIP_SAVE"},
default=False
)
updateIvy: BoolProperty(
name="Update Ivy",
description="Update the Ivy location based on the cursor and Panel settings",
default=False
)
defaultIvy: BoolProperty(
name="Default Ivy",
options={"HIDDEN", "SKIP_SAVE"},
default=False
)
@classmethod
def poll(self, context):
@ -510,6 +513,7 @@ class IvyGen(Operator):
# Get the selected object
ob = context.active_object
bvhtree = bvhtree_from_object(ob)
# Check if the mesh has at least one polygon since some functions
# are expecting them in the object's data (see T51753)
@ -562,7 +566,7 @@ class IvyGen(Operator):
(IVY.maxLength < maxLength) and
(not checkTime or (time.time() - t < maxTime))):
# Grow the ivy for this iteration
IVY.grow(ob)
IVY.grow(ob, bvhtree)
# Print the proportion of ivy growth to console
if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
@ -596,9 +600,8 @@ class CURVE_PT_IvyGenPanel(Panel):
bl_label = "Ivy Generator"
bl_idname = "CURVE_PT_IvyGenPanel"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Create"
bl_context = 'objectmode'
bl_region_type = "UI"
bl_category = "View"
bl_options = {"DEFAULT_CLOSED"}
def draw(self, context):
@ -615,26 +618,26 @@ class CURVE_PT_IvyGenPanel(Panel):
prop_def.updateIvy = True
col = layout.column(align=True)
col.label("Generation Settings:")
col.label(text="Generation Settings:")
col.prop(wm.ivy_gen_props, "randomSeed")
col.prop(wm.ivy_gen_props, "maxTime")
col = layout.column(align=True)
col.label("Size Settings:")
col.label(text="Size Settings:")
col.prop(wm.ivy_gen_props, "maxIvyLength")
col.prop(wm.ivy_gen_props, "ivySize")
col.prop(wm.ivy_gen_props, "maxFloatLength")
col.prop(wm.ivy_gen_props, "maxAdhesionDistance")
col = layout.column(align=True)
col.label("Weight Settings:")
col.label(text="Weight Settings:")
col.prop(wm.ivy_gen_props, "primaryWeight")
col.prop(wm.ivy_gen_props, "randomWeight")
col.prop(wm.ivy_gen_props, "gravityWeight")
col.prop(wm.ivy_gen_props, "adhesionWeight")
col = layout.column(align=True)
col.label("Branch Settings:")
col.label(text="Branch Settings:")
col.prop(wm.ivy_gen_props, "branchingProbability")
col.prop(wm.ivy_gen_props, "ivyBranchSize")
@ -643,124 +646,124 @@ class CURVE_PT_IvyGenPanel(Panel):
if wm.ivy_gen_props.growLeaves:
col = layout.column(align=True)
col.label("Leaf Settings:")
col.label(text="Leaf Settings:")
col.prop(wm.ivy_gen_props, "ivyLeafSize")
col.prop(wm.ivy_gen_props, "leafProbability")
class IvyGenProperties(PropertyGroup):
maxIvyLength = FloatProperty(
name="Max Ivy Length",
description="Maximum ivy length in Blender Units",
default=1.0,
min=0.0,
soft_max=3.0,
subtype='DISTANCE',
unit='LENGTH'
)
primaryWeight = FloatProperty(
name="Primary Weight",
description="Weighting given to the current direction",
default=0.5,
min=0.0,
soft_max=1.0
)
randomWeight = FloatProperty(
name="Random Weight",
description="Weighting given to the random direction",
default=0.2,
min=0.0,
soft_max=1.0
)
gravityWeight = FloatProperty(
name="Gravity Weight",
description="Weighting given to the gravity direction",
default=1.0,
min=0.0,
soft_max=1.0
)
adhesionWeight = FloatProperty(
name="Adhesion Weight",
description="Weighting given to the adhesion direction",
default=0.1,
min=0.0,
soft_max=1.0
)
branchingProbability = FloatProperty(
name="Branching Probability",
description="Probability of a new branch forming",
default=0.05,
min=0.0,
soft_max=1.0
)
leafProbability = FloatProperty(
name="Leaf Probability",
description="Probability of a leaf forming",
default=0.35,
min=0.0,
soft_max=1.0
)
ivySize = FloatProperty(
name="Ivy Size",
description="The length of an ivy segment in Blender"
" Units",
default=0.02,
min=0.0,
soft_max=1.0,
precision=3
)
ivyLeafSize = FloatProperty(
name="Ivy Leaf Size",
description="The size of the ivy leaves",
default=0.02,
min=0.0,
soft_max=0.5,
precision=3
)
ivyBranchSize = FloatProperty(
name="Ivy Branch Size",
description="The size of the ivy branches",
default=0.001,
min=0.0,
soft_max=0.1,
precision=4
)
maxFloatLength = FloatProperty(
name="Max Float Length",
description="The maximum distance that a branch "
"can live while floating",
default=0.5,
min=0.0,
soft_max=1.0
)
maxAdhesionDistance = FloatProperty(
name="Max Adhesion Length",
description="The maximum distance that a branch "
"will feel the effects of adhesion",
default=1.0,
min=0.0,
soft_max=2.0,
precision=2
)
randomSeed = IntProperty(
name="Random Seed",
description="The seed governing random generation",
default=0,
min=0
)
maxTime = FloatProperty(
name="Maximum Time",
description="The maximum time to run the generation for "
"in seconds generation (0.0 = Disabled)",
default=0.0,
min=0.0,
soft_max=10
)
growLeaves = BoolProperty(
name="Grow Leaves",
description="Grow leaves or not",
default=True
)
maxIvyLength: FloatProperty(
name="Max Ivy Length",
description="Maximum ivy length in Blender Units",
default=1.0,
min=0.0,
soft_max=3.0,
subtype='DISTANCE',
unit='LENGTH'
)
primaryWeight: FloatProperty(
name="Primary Weight",
description="Weighting given to the current direction",
default=0.5,
min=0.0,
soft_max=1.0
)
randomWeight: FloatProperty(
name="Random Weight",
description="Weighting given to the random direction",
default=0.2,
min=0.0,
soft_max=1.0
)
gravityWeight: FloatProperty(
name="Gravity Weight",
description="Weighting given to the gravity direction",
default=1.0,
min=0.0,
soft_max=1.0
)
adhesionWeight: FloatProperty(
name="Adhesion Weight",
description="Weighting given to the adhesion direction",
default=0.1,
min=0.0,
soft_max=1.0
)
branchingProbability: FloatProperty(
name="Branching Probability",
description="Probability of a new branch forming",
default=0.05,
min=0.0,
soft_max=1.0
)
leafProbability: FloatProperty(
name="Leaf Probability",
description="Probability of a leaf forming",
default=0.35,
min=0.0,
soft_max=1.0
)
ivySize: FloatProperty(
name="Ivy Size",
description="The length of an ivy segment in Blender"
" Units",
default=0.02,
min=0.0,
soft_max=1.0,
precision=3
)
ivyLeafSize: FloatProperty(
name="Ivy Leaf Size",
description="The size of the ivy leaves",
default=0.02,
min=0.0,
soft_max=0.5,
precision=3
)
ivyBranchSize: FloatProperty(
name="Ivy Branch Size",
description="The size of the ivy branches",
default=0.001,
min=0.0,
soft_max=0.1,
precision=4
)
maxFloatLength: FloatProperty(
name="Max Float Length",
description="The maximum distance that a branch "
"can live while floating",
default=0.5,
min=0.0,
soft_max=1.0
)
maxAdhesionDistance: FloatProperty(
name="Max Adhesion Length",
description="The maximum distance that a branch "
"will feel the effects of adhesion",
default=1.0,
min=0.0,
soft_max=2.0,
precision=2
)
randomSeed: IntProperty(
name="Random Seed",
description="The seed governing random generation",
default=0,
min=0
)
maxTime: FloatProperty(
name="Maximum Time",
description="The maximum time to run the generation for "
"in seconds generation (0.0 = Disabled)",
default=0.0,
min=0.0,
soft_max=10
)
growLeaves: BoolProperty(
name="Grow Leaves",
description="Grow leaves or not",
default=True
)
classes = (
@ -775,8 +778,8 @@ def register():
bpy.utils.register_class(cls)
bpy.types.WindowManager.ivy_gen_props = PointerProperty(
type=IvyGenProperties
)
type=IvyGenProperties
)
def unregister():

View File

@ -445,7 +445,7 @@ class Manipulator():
ret = False
self.keyboard_cancel(context, event)
pass
context.area.header_text_set("")
context.area.header_text_set(None)
self.keyboard_input_active = False
self.feedback.disable()
return ret

View File

@ -5,6 +5,7 @@
- Require Blender 2.80+.
- API change: `blender_id.get_subclient_user_id()` now returns `''` instead of `None` when the user
is not logged in.
- Log which Blender ID instance is communicated with.
# Version 1.5 (released 2018-07-03)

View File

@ -55,7 +55,12 @@ def blender_id_endpoint(endpoint_path=None):
import os
import urllib.parse
base_url = os.environ.get('BLENDER_ID_ENDPOINT', 'https://www.blender.org/id/')
base_url = os.environ.get('BLENDER_ID_ENDPOINT')
if base_url:
log.warning('Using overridden Blender ID url %s', base_url)
else:
base_url = 'https://www.blender.org/id/'
log.info('Using standard Blender ID url %s', base_url)
# urljoin() is None-safe for the 2nd parameter.
return urllib.parse.urljoin(base_url, endpoint_path)

View File

@ -307,9 +307,9 @@ def main(context, obj, options, curve_dimension):
# create new object and put into scene
newCurve = bpy.data.objects.new("Simple_" + obj.name, curve)
coll = context.view_layer.collections.active.collection
coll = context.view_layer.active_layer_collection.collection
coll.objects.link(newCurve)
newCurve.select_set('SELECT')
newCurve.select_set(True)
context.view_layer.objects.active = newCurve
newCurve.matrix_world = obj.matrix_world

View File

@ -407,7 +407,7 @@ class IV_OT_icons_show(bpy.types.Operator):
row.prop(
pr, "copy_on_select", text="",
icon='BORDER_RECT', toggle=True)
icon='COPYDOWN', toggle=True)
if pr.copy_on_select:
sub = row.row(align=True)
if bpy.context.window.screen.name == "temp":

View File

@ -348,7 +348,7 @@ def bvh_node_dict2objects(context, bvh_name, bvh_nodes, rotate_mode='NATIVE', fr
scene = context.scene
for obj in scene.objects:
obj.select_set("DESELECT")
obj.select_set(False)
objects = []
@ -356,7 +356,7 @@ def bvh_node_dict2objects(context, bvh_name, bvh_nodes, rotate_mode='NATIVE', fr
obj = bpy.data.objects.new(name, None)
context.collection.objects.link(obj)
objects.append(obj)
obj.select_set("SELECT")
obj.select_set(True)
# nicer drawing.
obj.empty_display_type = 'CUBE'
@ -422,14 +422,14 @@ def bvh_node_dict2armature(
# Add the new armature,
scene = context.scene
for obj in scene.objects:
obj.select_set("DESELECT")
obj.select_set(False)
arm_data = bpy.data.armatures.new(bvh_name)
arm_ob = bpy.data.objects.new(bvh_name, arm_data)
context.collection.objects.link(arm_ob)
arm_ob.select_set("SELECT")
arm_ob.select_set(True)
context.view_layer.objects.active = arm_ob
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

View File

@ -194,15 +194,15 @@ def updatemesh(objekti, proxy):
if(objekti.vertex_groups.keys() != []):
bpy.ops.object.select_all(action='DESELECT')
proxy.select_set('SELECT')
objekti.select_set('SELECT')
proxy.select_set(True)
objekti.select_set(True)
bpy.ops.object.vertex_group_copy_to_selected()
bpy.ops.object.select_all(action='DESELECT')
# UV Set Copy
proxy.select_set('SELECT')
objekti.select_set('SELECT')
proxy.select_set(True)
objekti.select_set(True)
if len(objekti.data.uv_layers) > 1:
obj_uv_index = objekti.data.uv_layers.active_index
@ -220,7 +220,7 @@ def updatemesh(objekti, proxy):
#Mesh Copy
proxy.select_set('SELECT')
proxy.select_set(True)
obj_data = objekti.data.id_data
objekti.data = proxy.data.id_data
objekti.data.id_data.name = obj_data.name
@ -566,7 +566,7 @@ class SCENE_OT_import(bpy.types.Operator):
objekti = bpy.data.objects[oname]
if(objekti.coat3D.import_mesh and coat3D.importmesh == True):
objekti.coat3D.import_mesh = False
objekti.select_set('SELECT')
objekti.select_set(True)
new_name = objekti.data.name
name_boxs = new_name.split('.')
@ -627,7 +627,7 @@ class SCENE_OT_import(bpy.types.Operator):
mat_list.append(obj_mat.material)
bpy.ops.object.select_all(action='DESELECT')
obj_proxy.select_set('SELECT')
obj_proxy.select_set(True)
bpy.ops.object.select_all(action='TOGGLE')
@ -650,7 +650,7 @@ class SCENE_OT_import(bpy.types.Operator):
#tärkee että saadaan oikein käännettyä objekt
objekti.select_set('SELECT')
objekti.select_set(True)
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
objekti.data.materials.pop()
@ -675,14 +675,14 @@ class SCENE_OT_import(bpy.types.Operator):
if(coat3D.importmesh and not(os.path.isfile(objekti.coat3D.applink_address))):
coat3D.importmesh = False
objekti.select_set('SELECT')
objekti.select_set(True)
if(coat3D.importtextures):
is_new = False
print('matlist: ', mat_list)
print('objekti: ', objekti)
print('is_new: ', is_new)
tex.matlab(objekti,mat_list,texturelist,is_new)
objekti.select_set('DESELECT')
objekti.select_set(False)
else:
mat_list = []
@ -696,13 +696,13 @@ class SCENE_OT_import(bpy.types.Operator):
print('objekti: ', objekti)
print('is_new: ', is_new)
tex.matlab(objekti,mat_list,texturelist, is_new)
objekti.select_set('DESELECT')
objekti.select_set(False)
bpy.ops.object.select_all(action='DESELECT')
if(import_list):
for del_obj in diff_objects:
bpy.context.collection.all_objects[del_obj].select_set('SELECT')
bpy.context.collection.all_objects[del_obj].select_set(True)
bpy.ops.object.delete()
@ -760,12 +760,12 @@ class SCENE_OT_import(bpy.types.Operator):
if(new_obj.coat3D.applink_old == False):
nimi += new_obj.data.name + ':::'
new_obj.select_set('SELECT')
new_obj.select_set(True)
#bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
new_obj.rotation_euler = (0, 0, 0)
new_obj.scale = (1, 1, 1)
new_obj.coat3D.applink_firsttime = False
new_obj.select_set('DESELECT')
new_obj.select_set(False)
new_obj.coat3D.applink_address = new_applink_address
new_obj.coat3D.objecttime = str(os.path.getmtime(new_obj.coat3D.applink_address))
splittext = ntpath.basename(new_applink_address)

View File

@ -47,9 +47,13 @@ SVGEmptyStyles = {'useFill': None,
def srgb_to_linearrgb(c):
if c < 0.04045:
return 0.0 if c < 0.0 else c * (1.0 / 12.92);
return 0.0 if c < 0.0 else c * (1.0 / 12.92)
else:
return pow((c + 0.055) * (1.0 / 1.055), 2.4);
return pow((c + 0.055) * (1.0 / 1.055), 2.4)
def check_points_equal(point_a, point_b):
return (abs(point_a[0] - point_b[0]) < 1e-6 and
abs(point_a[1] - point_b[1]) < 1e-6)
def SVGParseFloat(s, i=0):
@ -408,7 +412,7 @@ SVGTransforms = {'translate': SVGTransformTranslate,
def SVGParseStyles(node, context):
"""
Parse node to get different styles for displaying geometries
(materilas, filling flags, etc..)
(materials, filling flags, etc..)
"""
styles = SVGEmptyStyles.copy()
@ -645,7 +649,7 @@ class SVGPathParser:
# filled.
first = self._spline['points'][0]
if abs(first['x'] - x) < 1e-6 and abs(first['y'] - y) < 1e-6:
if check_points_equal((first['x'], first['y']), (x, y)):
if handle_left is not None:
first['handle_left'] = handle_left
first['handle_left_type'] = 'FREE'
@ -662,6 +666,9 @@ class SVGPathParser:
if last['handle_right_type'] == 'VECTOR' and handle_left_type == 'FREE':
last['handle_right'] = (last['x'], last['y'])
last['handle_right_type'] = 'FREE'
if last['handle_right_type'] == 'FREE' and handle_left_type == 'VECTOR':
handle_left = (x, y)
handle_left_type = 'FREE'
point = {'x': x,
'y': y,
@ -747,7 +754,7 @@ class SVGPathParser:
def _pathCurveToCS(self, code):
"""
Cubic BEZIER CurveTo path command
Cubic BEZIER CurveTo path command
"""
c = code.lower()
@ -784,7 +791,7 @@ class SVGPathParser:
def _pathCurveToQT(self, code):
"""
Qyadracic BEZIER CurveTo path command
Quadratic BEZIER CurveTo path command
"""
c = code.lower()
@ -796,20 +803,21 @@ class SVGPathParser:
else:
if self._handle is not None:
x1, y1 = SVGFlipHandle(self._point[0], self._point[1],
self._handle[0], self._handle[1])
self._handle[0], self._handle[1])
else:
x1, y1 = self._point
x, y = self._getCoordPair(code.islower(), self._point)
if self._spline is None:
self._appendPoint(self._point[0], self._point[1],
handle_left_type='FREE', handle_left=self._point,
handle_right_type='FREE', handle_right=self._point)
if not check_points_equal((x, y), self._point):
if self._spline is None:
self._appendPoint(self._point[0], self._point[1],
handle_left_type='FREE', handle_left=self._point,
handle_right_type='FREE', handle_right=self._point)
self._appendPoint(x, y,
handle_left_type='FREE', handle_left=(x1, y1),
handle_right_type='FREE', handle_right=(x, y))
self._appendPoint(x, y,
handle_left_type='FREE', handle_left=(x1, y1),
handle_right_type='FREE', handle_right=(x, y))
self._point = (x, y)
self._handle = (x1, y1)
@ -961,7 +969,7 @@ class SVGPathParser:
raise Exception('Unknown path command: {0}' . format(code))
if cmd in {'Z', 'z'}:
closed =True
closed = True
else:
closed = False
@ -1225,6 +1233,19 @@ class SVGGeometryPATH(SVGGeometry):
for spline in self._splines:
act_spline = None
if spline['closed'] and len(spline['points']) >= 2:
first = spline['points'][0]
last = spline['points'][-1]
if ( first['handle_left_type'] == 'FREE' and
last['handle_right_type'] == 'VECTOR'):
last['handle_right_type'] = 'FREE'
last['handle_right'] = (last['x'], last['y'])
if ( last['handle_right_type'] == 'FREE' and
first['handle_left_type'] == 'VECTOR'):
first['handle_left_type'] = 'FREE'
first['handle_left'] = (first['x'], first['y'])
for point in spline['points']:
co = self._transformCoord((point['x'], point['y']))

View File

@ -29,6 +29,7 @@ bl_info = {
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Add_Mesh/Planes_from_Images",
"support": 'OFFICIAL',
"category": "Import-Export",
}
@ -908,7 +909,7 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper):
# setup new selection
for plane in planes:
plane.select_set('SELECT')
plane.select_set(True)
# all done!
self.report({'INFO'}, "Added {} Image Plane(s)".format(len(planes)))

View File

@ -398,7 +398,7 @@ def load_ply(filepath):
obj = bpy.data.objects.new(ply_name, mesh)
bpy.context.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set("SELECT")
obj.select_set(True)
print('\nSuccessfully imported %r in %.3f sec' % (filepath, time.time() - t))
return {'FINISHED'}

View File

@ -21,7 +21,7 @@
bl_info = {
"name": "STL format",
"author": "Guillaume Bouchard (Guillaum)",
"version": (1, 1, 2),
"version": (1, 1, 3),
"blender": (2, 80, 0),
"location": "File > Import-Export > Stl",
"description": "Import-Export STL files",

View File

@ -60,7 +60,7 @@ def create_and_link_mesh(name, faces, face_nors, points, global_matrix):
obj = bpy.data.objects.new(name, mesh)
bpy.context.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set("SELECT")
obj.select_set(True)
def faces_from_mesh(ob, global_matrix, use_mesh_modifiers=False):
@ -84,7 +84,7 @@ def faces_from_mesh(ob, global_matrix, use_mesh_modifiers=False):
try:
mesh = ob.to_mesh(bpy.context.depsgraph, use_mesh_modifiers)
except RuntimeError:
raise StopIteration
return
mat = global_matrix @ ob.matrix_world
mesh.transform(mat)

View File

@ -1970,8 +1970,8 @@ class FbxImportHelperNode:
child.build_skeleton_children(fbx_tmpl, settings, scene, view_layer)
# instance in scene
view_layer.collections.active.collection.objects.link(obj)
obj.select_set('SELECT')
view_layer.active_layer_collection.collection.objects.link(obj)
obj.select_set(True)
return obj
@ -2097,8 +2097,8 @@ class FbxImportHelperNode:
blen_read_custom_properties(self.fbx_elem, arm, settings)
# instance in scene
view_layer.collections.active.collection.objects.link(arm)
arm.select_set('SELECT')
view_layer.active_layer_collection.collection.objects.link(arm)
arm.select_set(True)
# Add bones:
@ -2140,8 +2140,8 @@ class FbxImportHelperNode:
child.build_hierarchy(fbx_tmpl, settings, scene, view_layer)
# instance in scene
view_layer.collections.active.collection.objects.link(obj)
obj.select_set('SELECT')
view_layer.active_layer_collection.collection.objects.link(obj)
obj.select_set(True)
return obj
else:

View File

@ -24,7 +24,10 @@ import bpy
from mathutils import Matrix, Vector, Color
from bpy_extras import io_utils, node_shader_utils
from progress_report import ProgressReport, ProgressReportSubstep
from bpy_extras.wm_utils.progress_report import (
ProgressReport,
ProgressReportSubstep,
)
def name_compat(name):

View File

@ -36,10 +36,10 @@ import os
import time
import bpy
import mathutils
from bpy_extras.io_utils import unpack_list
from bpy_extras.image_utils import load_image
from progress_report import ProgressReport, ProgressReportSubstep
from bpy_extras.wm_utils.progress_report import ProgressReport
def line_value(line_split):
@ -1179,16 +1179,12 @@ def load(context,
create_nurbs(context_nurbs, verts_loc, new_objects)
view_layer = context.view_layer
if view_layer.collections.active:
collection = view_layer.collections.active.collection
else:
collection = scene.master_collection.new()
view_layer.collections.link(collection)
collection = view_layer.active_layer_collection.collection
# Create new obj
for obj in new_objects:
collection.objects.link(obj)
obj.select_set('SELECT')
obj.select_set(True)
# we could apply this anywhere before scaling.
obj.matrix_world = global_matrix

View File

@ -2884,7 +2884,7 @@ class Carver(bpy.types.Operator):
traceback.print_exc()
context.window.cursor_modal_set("DEFAULT")
context.area.header_text_set("")
context.area.header_text_set(None)
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
self.report({'WARNING'},

View File

@ -79,7 +79,7 @@ class OBJECT_OT_FastLoop(Operator):
def modal(self, context, event):
if event.type == 'ESC':
context.area.header_text_set("")
context.area.header_text_set(None)
return {'CANCELLED'}
elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE':

View File

@ -786,7 +786,7 @@ class OffsetEdges(Operator):
def restore_original_and_free(self, context):
self.caches_valid = False # Make caches invalid
context.area.header_text_set("")
context.area.header_text_set(None)
me = context.edit_object.data
bpy.ops.object.mode_set(mode="OBJECT")
@ -794,7 +794,7 @@ class OffsetEdges(Operator):
bpy.ops.object.mode_set(mode="EDIT")
self._bm_orig.free()
context.area.header_text_set("")
context.area.header_text_set(None)
def invoke(self, context, event):
# In edit mode

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
# Contact for more information about the Addon:
# Email: germano.costa@ig.com.br
# Twitter: wii_mano @mano_wii
bl_info = {
"name": "Snap_Utilities_Line",
"author": "Germano Cavalcante",
"version": (5, 8, 23),
"blender": (2, 80, 0),
"location": "View3D > TOOLS > Make Line",
"description": "Extends Blender Snap controls",
#"wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities",
"category": "Mesh"}
if "bpy" in locals():
import importlib
importlib.reload(preferences)
importlib.reload(ops_line)
importlib.reload(common_classes)
else:
from . import preferences
from . import ops_line
import bpy
from bpy.utils.toolsystem import ToolDef
@ToolDef.from_fn
def tool_make_line():
import os
def draw_settings(context, layout, tool):
addon_prefs = context.user_preferences.addons["mesh_snap_utilities_line"].preferences
layout.prop(addon_prefs, "incremental")
layout.prop(addon_prefs, "increments_grid")
if addon_prefs.increments_grid:
layout.prop(addon_prefs, "relative_scale")
layout.prop(addon_prefs, "create_face")
layout.prop(addon_prefs, "outer_verts")
#props = tool.operator_properties("mesh.snap_utilities_line")
#layout.prop(props, "radius")
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
return dict(
text="Make Line",
description=(
"Make Lines\n"
"Connect them to split faces"
),
icon=os.path.join(icons_dir, "ops.mesh.make_line"),
# widget="MESH_GGT_mouse_point",
operator="mesh.make_line",
keymap=(
("mesh.make_line", None, dict(type='ACTIONMOUSE', value='PRESS')),
),
draw_settings=draw_settings,
)
def register():
def get_tool_list(space_type, context_mode):
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
return cls._tools[context_mode]
bpy.utils.register_class(preferences.SnapUtilitiesLinePreferences)
bpy.utils.register_class(common_classes.VIEW3D_OT_rotate_custom_pivot)
bpy.utils.register_class(common_classes.VIEW3D_OT_zoom_custom_target)
bpy.utils.register_class(ops_line.SnapUtilitiesLine)
# bpy.utils.register_class(common_classes.MousePointWidget)
# bpy.utils.register_class(common_classes.MousePointWidgetGroup)
bpy.utils.register_tool('VIEW_3D', 'EDIT_MESH', tool_make_line)
# Move tool to after 'Add Cube'
tools = get_tool_list('VIEW_3D', 'EDIT_MESH')
for index, tool in enumerate(tools):
if isinstance(tool, ToolDef) and tool.text == "Add Cube":
break
tools.insert(index + 1, tools.pop(-1))
def unregister():
bpy.utils.unregister_tool('VIEW_3D', 'EDIT_MESH', tool_make_line)
# bpy.utils.unregister_class(common_classes.MousePointWidgetGroup)
# bpy.utils.unregister_class(common_classes.MousePointWidget)
bpy.utils.unregister_class(ops_line.SnapUtilitiesLine)
bpy.utils.unregister_class(common_classes.VIEW3D_OT_zoom_custom_target)
bpy.utils.unregister_class(common_classes.VIEW3D_OT_rotate_custom_pivot)
bpy.utils.unregister_class(preferences.SnapUtilitiesLinePreferences)
if __name__ == "__main__":
__name__ = "mesh_snap_utilities_line"
__package__ = "mesh_snap_utilities_line"
register()

View File

@ -0,0 +1,510 @@
### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
import bgl
import gpu
import numpy as np
from .common_utilities import snap_utilities
class SnapDrawn():
def __init__(self, out_color, face_color,
edge_color, vert_color, center_color,
perpendicular_color, constrain_shift_color,
axis_x_color, axis_y_color, axis_z_color):
self.out_color = out_color
self.face_color = face_color
self.edge_color = edge_color
self.vert_color = vert_color
self.center_color = center_color
self.perpendicular_color = perpendicular_color
self.constrain_shift_color = constrain_shift_color
self.axis_x_color = axis_x_color
self.axis_y_color = axis_y_color
self.axis_z_color = axis_z_color
self._format_pos = gpu.types.GPUVertFormat()
self._format_pos.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
self._format_pos_and_color = gpu.types.GPUVertFormat()
self._format_pos_and_color.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
self._format_pos_and_color.attr_add(id="color", comp_type='F32', len=4, fetch_mode='FLOAT')
self._program_unif_col = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
self._program_smooth_col = gpu.shader.from_builtin("3D_SMOOTH_COLOR")
self._batch_point = None
self._batch_circle = None
self._batch_vector = None
def batch_line_strip_create(self, coords):
vbo = gpu.types.GPUVertBuf(self._format_pos, len = len(coords))
vbo.attr_fill(0, data = coords)
batch_lines = gpu.types.GPUBatch(type = "LINE_STRIP", buf = vbo)
return batch_lines
def batch_lines_smooth_color_create(self, coords, colors):
vbo = gpu.types.GPUVertBuf(self._format_pos_and_color, len = len(coords))
vbo.attr_fill(0, data = coords)
vbo.attr_fill(1, data = colors)
batch_lines = gpu.types.GPUBatch(type = "LINES", buf = vbo)
return batch_lines
def batch_triangles_create(self, coords):
vbo = gpu.types.GPUVertBuf(self._format_pos, len = len(coords))
vbo.attr_fill(0, data = coords)
batch_tris = gpu.types.GPUBatch(type = "TRIS", buf = vbo)
return batch_tris
def batch_point_get(self):
if self._batch_point is None:
vbo = gpu.types.GPUVertBuf(self._format_pos, len = 1)
vbo.attr_fill(0, ((0.0, 0.0, 0.0),))
self._batch_point = gpu.types.GPUBatch(type = "POINTS", buf = vbo)
return self._batch_point
def draw(self, type, location, list_verts_co, vector_constrain, prevloc):
# draw 3d point OpenGL in the 3D View
bgl.glEnable(bgl.GL_BLEND)
gpu.matrix.push()
self._program_unif_col.bind()
if list_verts_co:
# draw 3d line OpenGL in the 3D View
bgl.glDepthRange(0, 0.9999)
bgl.glLineWidth(3.0)
batch = self.batch_line_strip_create([v.to_tuple() for v in list_verts_co] + [location.to_tuple()])
self._program_unif_col.uniform_float("color", (1.0, 0.8, 0.0, 0.5))
batch.draw(self._program_unif_col)
del batch
bgl.glDisable(bgl.GL_DEPTH_TEST)
point_batch = self.batch_point_get()
if vector_constrain:
if prevloc:
bgl.glPointSize(5)
gpu.matrix.translate(prevloc)
self._program_unif_col.uniform_float("color", (1.0, 1.0, 1.0, 0.5))
point_batch.draw(self._program_unif_col)
gpu.matrix.translate(-prevloc)
if vector_constrain[2] == 'X':
Color4f = self.axis_x_color
elif vector_constrain[2] == 'Y':
Color4f = self.axis_y_color
elif vector_constrain[2] == 'Z':
Color4f = self.axis_z_color
else:
Color4f = self.constrain_shift_color
else:
if type == 'OUT':
Color4f = self.out_color
elif type == 'FACE':
Color4f = self.face_color
elif type == 'EDGE':
Color4f = self.edge_color
elif type == 'VERT':
Color4f = self.vert_color
elif type == 'CENTER':
Color4f = self.center_color
elif type == 'PERPENDICULAR':
Color4f = self.perpendicular_color
else: # type == None
Color4f = self.out_color
bgl.glPointSize(10)
gpu.matrix.translate(location)
self._program_unif_col.uniform_float("color", Color4f)
point_batch.draw(self._program_unif_col)
# restore opengl defaults
bgl.glDepthRange(0.0, 1.0)
bgl.glPointSize(1.0)
bgl.glLineWidth(1.0)
bgl.glEnable(bgl.GL_DEPTH_TEST)
bgl.glDisable(bgl.GL_BLEND)
gpu.matrix.pop()
def draw_elem(self, snap_obj, bm, elem):
from bmesh.types import(
BMVert,
BMEdge,
BMFace,
)
# draw 3d point OpenGL in the 3D View
bgl.glEnable(bgl.GL_BLEND)
bgl.glDisable(bgl.GL_DEPTH_TEST)
with gpu.matrix.push_pop():
gpu.matrix.multiply_matrix(snap_obj.mat)
if isinstance(elem, BMVert):
if elem.link_edges:
color = self.vert_color
edges = np.empty((len(elem.link_edges), 2), [("pos", "f4", 3), ("color", "f4", 4)])
edges["pos"][:, 0] = elem.co
edges["pos"][:, 1] = [e.other_vert(elem).co for e in elem.link_edges]
edges["color"][:, 0] = color
edges["color"][:, 1] = (color[0], color[1], color[2], 0.0)
edges.shape = -1
self._program_smooth_col.bind()
bgl.glLineWidth(3.0)
batch = self.batch_lines_smooth_color_create(edges["pos"], edges["color"])
batch.draw(self._program_smooth_col)
bgl.glLineWidth(1.0)
else:
self._program_unif_col.bind()
if isinstance(elem, BMEdge):
self._program_unif_col.uniform_float("color", self.edge_color)
bgl.glLineWidth(3.0)
batch = self.batch_line_strip_create([v.co for v in elem.verts])
batch.draw(self._program_unif_col)
bgl.glLineWidth(1.0)
elif isinstance(elem, BMFace):
if len(snap_obj.data) == 2:
face_color = self.face_color[0], self.face_color[1], self.face_color[2], self.face_color[3] * 0.2
self._program_unif_col.uniform_float("color", face_color)
tris = snap_obj.data[1].get_loop_tri_co_by_bmface(bm, elem)
tris.shape = (-1, 3)
batch = self.batch_triangles_create(tris)
batch.draw(self._program_unif_col)
# restore opengl defaults
bgl.glEnable(bgl.GL_DEPTH_TEST)
bgl.glDisable(bgl.GL_BLEND)
class SnapNavigation():
@staticmethod
def debug_key(key):
for member in dir(key):
print(member, getattr(key, member))
@staticmethod
def convert_to_flag(shift, ctrl, alt):
return (shift << 0) | (ctrl << 1) | (alt << 2)
def __init__(self, context, use_ndof):
# TO DO:
# 'View Orbit', 'View Pan', 'NDOF Orbit View', 'NDOF Pan View'
self.use_ndof = use_ndof and context.user_preferences.inputs.use_ndof
self._rotate = set()
self._move = set()
self._zoom = set()
if self.use_ndof:
self._ndof_all = set()
self._ndof_orbit = set()
self._ndof_orbit_zoom = set()
self._ndof_pan = set()
for key in context.window_manager.keyconfigs.user.keymaps['3D View'].keymap_items:
if key.idname == 'view3d.rotate':
self._rotate.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value))
elif key.idname == 'view3d.move':
self._move.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value))
elif key.idname == 'view3d.zoom':
if key.type == 'WHEELINMOUSE':
self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), 'WHEELUPMOUSE', key.value, key.properties.delta))
elif key.type == 'WHEELOUTMOUSE':
self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), 'WHEELDOWNMOUSE', key.value, key.properties.delta))
else:
self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value, key.properties.delta))
elif self.use_ndof:
if key.idname == 'view3d.ndof_all':
self._ndof_all.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
elif key.idname == 'view3d.ndof_orbit':
self._ndof_orbit.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
elif key.idname == 'view3d.ndof_orbit_zoom':
self._ndof_orbit_zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
elif key.idname == 'view3d.ndof_pan':
self._ndof_pan.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
def run(self, context, event, snap_location):
evkey = (self.convert_to_flag(event.shift, event.ctrl, event.alt), event.type, event.value)
if evkey in self._rotate:
if snap_location:
bpy.ops.view3d.rotate_custom_pivot('INVOKE_DEFAULT', pivot=snap_location)
else:
bpy.ops.view3d.rotate('INVOKE_DEFAULT', use_mouse_init=True)
return True
if evkey in self._move:
#if event.shift and self.vector_constrain and \
# self.vector_constrain[2] in {'RIGHT_SHIFT', 'LEFT_SHIFT', 'shift'}:
# self.vector_constrain = None
bpy.ops.view3d.move('INVOKE_DEFAULT')
return True
for key in self._zoom:
if evkey == key[0:3]:
if snap_location and key[3]:
bpy.ops.view3d.zoom_custom_target('INVOKE_DEFAULT', delta=key[3], target=snap_location)
else:
bpy.ops.view3d.zoom('INVOKE_DEFAULT', delta=key[3])
return True
if self.use_ndof:
ndofkey = evkey[:2]
if evkey in self._ndof_all:
bpy.ops.view3d.ndof_all('INVOKE_DEFAULT')
return True
if evkey in self._ndof_orbit:
bpy.ops.view3d.ndof_orbit('INVOKE_DEFAULT')
return True
if evkey in self._ndof_orbit_zoom:
bpy.ops.view3d.ndof_orbit_zoom('INVOKE_DEFAULT')
return True
if evkey in self._ndof_pan:
bpy.ops.view3d.ndof_pan('INVOKE_DEFAULT')
return True
return False
class CharMap:
ascii = {
".", ",", "-", "+", "1", "2", "3",
"4", "5", "6", "7", "8", "9", "0",
"c", "m", "d", "k", "h", "a",
" ", "/", "*", "'", "\""
# "="
}
type = {
'BACK_SPACE', 'DEL',
'LEFT_ARROW', 'RIGHT_ARROW'
}
@staticmethod
def modal(self, context, event):
c = event.ascii
if c:
if c == ",":
c = "."
self.length_entered = self.length_entered[:self.line_pos] + c + self.length_entered[self.line_pos:]
self.line_pos += 1
if self.length_entered:
if event.type == 'BACK_SPACE':
self.length_entered = self.length_entered[:self.line_pos - 1] + self.length_entered[self.line_pos:]
self.line_pos -= 1
elif event.type == 'DEL':
self.length_entered = self.length_entered[:self.line_pos] + self.length_entered[self.line_pos + 1:]
elif event.type == 'LEFT_ARROW':
self.line_pos = (self.line_pos - 1) % (len(self.length_entered) + 1)
elif event.type == 'RIGHT_ARROW':
self.line_pos = (self.line_pos + 1) % (len(self.length_entered) + 1)
g_snap_widget = [None]
class MousePointWidget(bpy.types.Gizmo):
bl_idname = "VIEW3D_GT_mouse_point"
__slots__ = (
"sctx",
"bm",
"draw_cache",
"geom",
"incremental",
"preferences",
"loc",
"snap_obj",
"snap_to_grid",
"type",
)
def test_select(self, context, mval):
#print('test_select', mval)
self.snap_obj, prev_loc, self.loc, self.type, self.bm, self.geom, len = snap_utilities(
self.sctx,
None,
mval,
increment=self.incremental
)
context.area.tag_redraw()
return False
def draw(self, context):
if self.bm:
self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
self.draw_cache.draw(self.type, self.loc, None, None, None)
def setup(self):
if not hasattr(self, "sctx"):
global g_snap_widget
g_snap_widget[0] = self
context = bpy.context
self.preferences = preferences = context.user_preferences.addons[__package__].preferences
#Configure the unit of measure
self.snap_to_grid = preferences.increments_grid
self.incremental = bpy.utils.units.to_value(
context.scene.unit_settings.system, 'LENGTH', str(preferences.incremental))
self.draw_cache = SnapDrawn(
preferences.out_color,
preferences.face_color,
preferences.edge_color,
preferences.vert_color,
preferences.center_color,
preferences.perpendicular_color,
preferences.constrain_shift_color,
(*context.user_preferences.themes[0].user_interface.axis_x, 1.0),
(*context.user_preferences.themes[0].user_interface.axis_y, 1.0),
(*context.user_preferences.themes[0].user_interface.axis_z, 1.0)
)
#Init Snap Context
from .snap_context_l import SnapContext
from mathutils import Vector
self.sctx = SnapContext(context.region, context.space_data)
self.sctx.set_pixel_dist(12)
self.sctx.use_clip_planes(True)
if preferences.outer_verts:
for base in context.visible_bases:
self.sctx.add_obj(base.object, base.object.matrix_world)
self.sctx.set_snap_mode(True, True, True)
self.bm = None
self.type = 'OUT'
self.loc = Vector()
def __del__(self):
global g_snap_widget
g_snap_widget[0] = None
class MousePointWidgetGroup(bpy.types.GizmoGroup):
bl_idname = "MESH_GGT_mouse_point"
bl_label = "Draw Mouse Point"
bl_space_type = 'VIEW_3D'
bl_region_type = 'WINDOW'
bl_options = {'3D'}
def setup(self, context):
snap_widget = self.gizmos.new(MousePointWidget.bl_idname)
props = snap_widget.target_set_operator("mesh.make_line")
props.wait_for_input = False
class VIEW3D_OT_rotate_custom_pivot(bpy.types.Operator):
bl_idname = "view3d.rotate_custom_pivot"
bl_label = "Rotate the view"
bl_options = {'BLOCKING', 'GRAB_CURSOR'}
pivot: bpy.props.FloatVectorProperty("Pivot", subtype='XYZ')
g_up_axis: bpy.props.FloatVectorProperty("up_axis", default=(0.0, 0.0, 1.0), subtype='XYZ')
sensitivity: bpy.props.FloatProperty("sensitivity", default=0.007)
def modal(self, context, event):
from mathutils import Matrix
if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
dx = self.init_coord[0] - event.mouse_region_x
dy = self.init_coord[1] - event.mouse_region_y
rot_ver = Matrix.Rotation(-dx * self.sensitivity, 3, self.g_up_axis)
rot_hor = Matrix.Rotation(dy * self.sensitivity, 3, self.view_rot[0])
rot_mat = rot_hor @ rot_ver
view_matrix = self.view_rot @ rot_mat
pos = self.pos1 @ rot_mat + self.pivot
qua = view_matrix.to_quaternion()
qua.invert()
self.rv3d.view_location = pos
self.rv3d.view_rotation = qua
context.area.tag_redraw()
return {'RUNNING_MODAL'}
return {'FINISHED'}
def invoke(self, context, event):
self.rv3d = context.region_data
self.init_coord = event.mouse_region_x, event.mouse_region_y
self.pos1 = self.rv3d.view_location - self.pivot
self.view_rot = self.rv3d.view_matrix.to_3x3()
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
class VIEW3D_OT_zoom_custom_target(bpy.types.Operator):
bl_idname = "view3d.zoom_custom_target"
bl_label = "Zoom the view"
bl_options = {'BLOCKING', 'GRAB_CURSOR'}
target: bpy.props.FloatVectorProperty("target", subtype='XYZ')
delta: bpy.props.IntProperty("delta", default=0)
step_factor = 0.333
def modal(self, context, event):
if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
if not hasattr(self, "init_mouse_region_y"):
self.init_mouse_region_y = event.mouse_region_y
self.heigt_up = context.area.height - self.init_mouse_region_y
self.rv3d.view_location = self.target
fac = (event.mouse_region_y - self.init_mouse_region_y) / self.heigt_up
ret = 'RUNNING_MODAL'
else:
fac = self.step_factor * self.delta
ret = 'FINISHED'
self.rv3d.view_location = self.init_loc + (self.target - self.init_loc) * fac
self.rv3d.view_distance = self.init_dist - self.init_dist * fac
context.area.tag_redraw()
return {ret}
def invoke(self, context, event):
v3d = context.space_data
dist_range = (v3d.clip_start, v3d.clip_end)
self.rv3d = context.region_data
self.init_dist = self.rv3d.view_distance
if ((self.delta <= 0 and self.init_dist < dist_range[1]) or
(self.delta > 0 and self.init_dist > dist_range[0])):
self.init_loc = self.rv3d.view_location.copy()
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
return {'FINISHED'}

View File

@ -0,0 +1,269 @@
### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
#python tip: from-imports don't save memory.
#They execute and cache the entire module just like a regular import.
import bpy
import bmesh
from .snap_context_l import SnapContext
from mathutils import Vector
from mathutils.geometry import (
intersect_point_line,
intersect_line_line,
intersect_line_plane,
intersect_ray_tri,
)
def get_units_info(scale, unit_system, separate_units):
if unit_system == 'METRIC':
scale_steps = ((1000, 'km'), (1, 'm'), (1 / 100, 'cm'),
(1 / 1000, 'mm'), (1 / 1000000, '\u00b5m'))
elif unit_system == 'IMPERIAL':
scale_steps = ((5280, 'mi'), (1, '\''),
(1 / 12, '"'), (1 / 12000, 'thou'))
scale /= 0.3048 # BU to feet
else:
scale_steps = ((1, ' BU'),)
separate_units = False
return (scale, scale_steps, separate_units)
def convert_distance(val, units_info, precision=5):
scale, scale_steps, separate_units = units_info
sval = val * scale
idx = 0
while idx < len(scale_steps) - 1:
if sval >= scale_steps[idx][0]:
break
idx += 1
factor, suffix = scale_steps[idx]
sval /= factor
if not separate_units or idx == len(scale_steps) - 1:
dval = str(round(sval, precision)) + suffix
else:
ival = int(sval)
dval = str(round(ival, precision)) + suffix
fval = sval - ival
idx += 1
while idx < len(scale_steps):
fval *= scale_steps[idx - 1][0] / scale_steps[idx][0]
if fval >= 1:
dval += ' ' \
+ ("%.1f" % fval) \
+ scale_steps[idx][1]
break
idx += 1
return dval
def location_3d_to_region_2d(region, rv3d, coord):
prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0))
width_half = region.width / 2.0
height_half = region.height / 2.0
return Vector((width_half + width_half * (prj.x / prj.w),
height_half + height_half * (prj.y / prj.w),
prj.z / prj.w
))
def out_Location(rv3d, orig, vector):
view_matrix = rv3d.view_matrix
v1 = (int(view_matrix[0][0]*1.5), int(view_matrix[0][1]*1.5), int(view_matrix[0][2]*1.5))
v2 = (int(view_matrix[1][0]*1.5), int(view_matrix[1][1]*1.5), int(view_matrix[1][2]*1.5))
hit = intersect_ray_tri((1,0,0), (0,1,0), (0,0,0), (vector), (orig), False)
if hit is None:
hit = intersect_ray_tri(v1, v2, (0,0,0), (vector), (orig), False)
if hit is None:
hit = intersect_ray_tri(v1, v2, (0,0,0), (-vector), (orig), False)
if hit is None:
hit = Vector()
return hit
def get_snap_bm_geom(sctx, main_snap_obj, mcursor):
r_snp_obj, r_loc, r_elem, r_elem_co = sctx.snap_get(mcursor, main_snap_obj)
r_view_vector, r_orig = sctx.last_ray
r_bm = None
r_bm_geom = None
if r_snp_obj is not None:
obj = r_snp_obj.data[0]
if obj.type == 'MESH' and obj.data.is_editmode:
r_bm = bmesh.from_edit_mesh(obj.data)
if len(r_elem) == 1:
r_bm_geom = r_bm.verts[r_elem[0]]
elif len(r_elem) == 2:
try:
v1 = r_bm.verts[r_elem[0]]
v2 = r_bm.verts[r_elem[1]]
r_bm_geom = r_bm.edges.get([v1, v2])
except IndexError:
r_bm.verts.ensure_lookup_table()
elif len(r_elem) == 3:
tri = [
r_bm.verts[r_elem[0]],
r_bm.verts[r_elem[1]],
r_bm.verts[r_elem[2]],
]
faces = set(tri[0].link_faces).intersection(tri[1].link_faces, tri[2].link_faces)
if len(faces) == 1:
r_bm_geom = faces.pop()
else:
i = -2
edge = None
while not edge and i != 1:
edge = r_bm.edges.get([tri[i], tri[i + 1]])
i += 1
if edge:
for l in edge.link_loops:
if l.link_loop_next.vert == tri[i] or l.link_loop_prev.vert == tri[i - 2]:
r_bm_geom = l.face
break
return r_snp_obj, r_loc, r_elem, r_elem_co, r_view_vector, r_orig, r_bm, r_bm_geom
class SnapCache:
snp_obj = None
elem = None
v0 = None
v1 = None
vmid = None
vperp = None
v2d0 = None
v2d1 = None
v2dmid = None
v2dperp = None
is_increment = False
def snap_utilities(
sctx, main_snap_obj,
mcursor,
constrain = None,
previous_vert = None,
increment = 0.0):
snp_obj, loc, elem, elem_co, view_vector, orig, bm, bm_geom = get_snap_bm_geom(sctx, main_snap_obj, mcursor)
is_increment = False
r_loc = None
r_type = None
r_len = 0.0
if not snp_obj:
is_increment = True
if constrain:
end = orig + view_vector
t_loc = intersect_line_line(constrain[0], constrain[1], orig, end)
if t_loc is None:
t_loc = constrain
r_loc = t_loc[0]
else:
r_type = 'OUT'
r_loc = out_Location(sctx.rv3d, orig, view_vector)
elif len(elem) == 1:
r_type = 'VERT'
if constrain:
r_loc = intersect_point_line(loc, constrain[0], constrain[1])[0]
else:
r_loc = loc
elif len(elem) == 2:
if SnapCache.snp_obj is not snp_obj or not (elem == SnapCache.elem).all():
SnapCache.snp_obj = snp_obj
SnapCache.elem = elem
SnapCache.v0 = elem_co[0]
SnapCache.v1 = elem_co[1]
SnapCache.vmid = 0.5 * (SnapCache.v0 + SnapCache.v1)
SnapCache.v2d0 = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.v0)
SnapCache.v2d1 = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.v1)
SnapCache.v2dmid = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.vmid)
if previous_vert and (not bm_geom or previous_vert not in bm_geom.verts):
pvert_co = main_snap_obj.mat @ previous_vert.co
perp_point = intersect_point_line(pvert_co, SnapCache.v0, SnapCache.v1)
SnapCache.vperp = perp_point[0]
#factor = point_perpendicular[1]
SnapCache.v2dperp = location_3d_to_region_2d(sctx.region, sctx.rv3d, perp_point[0])
SnapCache.is_increment = False
else:
SnapCache.is_increment = True
#else: SnapCache.v2dperp = None
if constrain:
t_loc = intersect_line_line(constrain[0], constrain[1], SnapCache.v0, SnapCache.v1)
if t_loc is None:
is_increment = True
end = orig + view_vector
t_loc = intersect_line_line(constrain[0], constrain[1], orig, end)
r_loc = t_loc[0]
elif SnapCache.v2dperp and\
abs(SnapCache.v2dperp[0] - mcursor[0]) < 10 and abs(SnapCache.v2dperp[1] - mcursor[1]) < 10:
r_type = 'PERPENDICULAR'
r_loc = SnapCache.vperp
elif abs(SnapCache.v2dmid[0] - mcursor[0]) < 10 and abs(SnapCache.v2dmid[1] - mcursor[1]) < 10:
r_type = 'CENTER'
r_loc = SnapCache.vmid
else:
is_increment = SnapCache.is_increment
r_type = 'EDGE'
r_loc = loc
elif len(elem) == 3:
r_type = 'FACE'
if constrain:
is_increment = False
r_loc = intersect_point_line(loc, constrain[0], constrain[1])[0]
else:
is_increment = True
r_loc = loc
if previous_vert:
pv_co = main_snap_obj.mat @ previous_vert.co
vec = r_loc - pv_co
if is_increment and increment:
r_len = round((1 / increment) * vec.length) * increment
r_loc = r_len * vec.normalized() + pv_co
else:
r_len = vec.length
return snp_obj, loc, r_loc, r_type, bm, bm_geom, r_len
snap_utilities.cache = SnapCache

Binary file not shown.

View File

@ -0,0 +1,553 @@
### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bpy, bmesh
from bpy.props import FloatProperty
from mathutils import Vector
from mathutils.geometry import intersect_point_line
from .common_classes import (
SnapDrawn,
CharMap,
SnapNavigation,
g_snap_widget, #TODO: remove
)
from .common_utilities import (
get_units_info,
convert_distance,
snap_utilities,
)
if not __package__:
__package__ = "mesh_snap_utilities"
def get_closest_edge(bm, point, dist):
r_edge = None
for edge in bm.edges:
v1 = edge.verts[0].co
v2 = edge.verts[1].co
# Test the BVH (AABB) first
for i in range(3):
if v1[i] <= v2[i]:
isect = v1[i] - dist <= point[i] <= v2[i] + dist
else:
isect = v2[i] - dist <= point[i] <= v1[i] + dist
if not isect:
break
else:
ret = intersect_point_line(point, v1, v2)
if ret[1] < 0.0:
tmp = v1
elif ret[1] > 1.0:
tmp = v2
else:
tmp = ret[0]
new_dist = (point - tmp).length
if new_dist <= dist:
dist = new_dist
r_edge = edge
return r_edge
def get_loose_linked_edges(bmvert):
linked = [e for e in bmvert.link_edges if not e.link_faces]
for e in linked:
linked += [le for v in e.verts if not v.link_faces for le in v.link_edges if le not in linked]
return linked
def draw_line(self, bm_geom, location):
obj = self.main_snap_obj.data[0]
bm = self.main_bm
split_faces = set()
update_edit_mesh = False
if bm_geom is None:
vert = bm.verts.new(location)
self.list_verts.append(vert)
update_edit_mesh = True
elif isinstance(bm_geom, bmesh.types.BMVert):
if (bm_geom.co - location).length_squared < .001:
if self.list_verts == [] or self.list_verts[-1] != bm_geom:
self.list_verts.append(bm_geom)
else:
vert = bm.verts.new(location)
self.list_verts.append(vert)
update_edit_mesh = True
elif isinstance(bm_geom, bmesh.types.BMEdge):
self.list_edges.append(bm_geom)
ret = intersect_point_line(location, bm_geom.verts[0].co, bm_geom.verts[1].co)
if (ret[0] - location).length_squared < .001:
if ret[1] == 0.0:
vert = bm_geom.verts[0]
elif ret[1] == 1.0:
vert = bm_geom.verts[1]
else:
edge, vert = bmesh.utils.edge_split(bm_geom, bm_geom.verts[0], ret[1])
update_edit_mesh = True
self.list_verts.append(vert)
self.geom = vert # hack to highlight in the drawing
# self.list_edges.append(edge)
else: # constrain point is near
vert = bm.verts.new(location)
self.list_verts.append(vert)
update_edit_mesh = True
elif isinstance(bm_geom, bmesh.types.BMFace):
split_faces.add(bm_geom)
vert = bm.verts.new(location)
self.list_verts.append(vert)
update_edit_mesh = True
# draw, split and create face
if len(self.list_verts) >= 2:
v1, v2 = self.list_verts[-2:]
edge = bm.edges.get([v1, v2])
if edge:
self.list_edges.append(edge)
else:
if not v2.link_edges:
edge = bm.edges.new([v1, v2])
self.list_edges.append(edge)
else: # split face
v1_link_faces = v1.link_faces
v2_link_faces = v2.link_faces
if v1_link_faces and v2_link_faces:
split_faces.update(set(v1_link_faces).intersection(v2_link_faces))
else:
if v1_link_faces:
faces = v1_link_faces
co2 = v2.co.copy()
else:
faces = v2_link_faces
co2 = v1.co.copy()
for face in faces:
if bmesh.geometry.intersect_face_point(face, co2):
co = co2 - face.calc_center_median()
if co.dot(face.normal) < 0.001:
split_faces.add(face)
if split_faces:
edge = bm.edges.new([v1, v2])
self.list_edges.append(edge)
ed_list = get_loose_linked_edges(v2)
for face in split_faces:
facesp = bmesh.utils.face_split_edgenet(face, ed_list)
del split_faces
else:
if self.intersect:
facesp = bmesh.ops.connect_vert_pair(bm, verts=[v1, v2], verts_exclude=bm.verts)
# print(facesp)
if not self.intersect or not facesp['edges']:
edge = bm.edges.new([v1, v2])
self.list_edges.append(edge)
else:
for edge in facesp['edges']:
self.list_edges.append(edge)
update_edit_mesh = True
# create face
if self.create_face:
ed_list = set(self.list_edges)
for edge in v2.link_edges:
for vert in edge.verts:
if vert != v2 and vert in self.list_verts:
ed_list.add(edge)
break
else:
continue
# Inner loop had a break, break the outer
break
ed_list.update(get_loose_linked_edges(v2))
bmesh.ops.edgenet_fill(bm, edges=list(ed_list))
update_edit_mesh = True
# print('face created')
if update_edit_mesh:
obj.data.update_gpu_tag()
obj.data.update_tag()
obj.update_from_editmode()
obj.update_tag()
bmesh.update_edit_mesh(obj.data)
self.sctx.tag_update_drawn_snap_object(self.main_snap_obj)
#bm.verts.index_update()
if not self.wait_for_input:
bpy.ops.ed.undo_push(message="Undo draw line*")
return [obj.matrix_world @ v.co for v in self.list_verts]
class SnapUtilitiesLine(bpy.types.Operator):
"""Make Lines. Connect them to split faces"""
bl_idname = "mesh.make_line"
bl_label = "Line Tool"
bl_options = {'REGISTER'}
wait_for_input: bpy.props.BoolProperty(name="Wait for Input", default=True)
constrain_keys = {
'X': Vector((1,0,0)),
'Y': Vector((0,1,0)),
'Z': Vector((0,0,1)),
'RIGHT_SHIFT': 'shift',
'LEFT_SHIFT': 'shift',
}
def _exit(self, context):
del self.main_bm #avoids unpredictable crashs
del self.bm
del self.list_edges
del self.list_verts
del self.list_verts_co
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
context.area.header_text_set(None)
if not self.snap_widget:
self.sctx.free()
del self.draw_cache
else:
self.sctx = None
self.draw_cache = None
#Restore initial state
context.tool_settings.mesh_select_mode = self.select_mode
context.space_data.overlay.show_face_center = self.show_face_center
def modal(self, context, event):
if self.navigation_ops.run(context, event, self.prevloc if self.vector_constrain else self.location):
return {'RUNNING_MODAL'}
context.area.tag_redraw()
if event.ctrl and event.type == 'Z' and event.value == 'PRESS':
if self.bm:
self.bm.free()
self.bm = None
if self.main_bm:
self.main_bm.free()
bpy.ops.ed.undo()
self.vector_constrain = None
self.list_verts_co = []
self.list_verts = []
self.list_edges = []
bpy.ops.object.mode_set(mode='EDIT') # just to be sure
self.main_bm = bmesh.from_edit_mesh(self.main_snap_obj.data[0].data)
self.sctx.tag_update_drawn_snap_object(self.main_snap_obj)
return {'RUNNING_MODAL'}
is_making_lines = bool(self.list_verts_co)
if event.type == 'MOUSEMOVE' or self.bool_update:
if self.rv3d.view_matrix != self.rotMat:
self.rotMat = self.rv3d.view_matrix.copy()
self.bool_update = True
snap_utilities.cache.snp_obj = None # hack for snap edge elemens update
else:
self.bool_update = False
mval = Vector((event.mouse_region_x, event.mouse_region_y))
self.snap_obj, self.prevloc, self.location, self.type, self.bm, self.geom, self.len = snap_utilities(
self.sctx,
self.main_snap_obj,
mval,
constrain=self.vector_constrain,
previous_vert=(self.list_verts[-1] if self.list_verts else None),
increment=self.incremental
)
if self.snap_to_grid and self.type == 'OUT':
loc = self.location / self.rd
self.location = Vector((round(loc.x),
round(loc.y),
round(loc.z))) * self.rd
if self.keyf8 and is_making_lines:
lloc = self.list_verts_co[-1]
view_vec, orig = self.sctx.last_ray
location = intersect_point_line(lloc, orig, (orig + view_vec))
vec = (location[0] - lloc)
ax, ay, az = abs(vec.x), abs(vec.y), abs(vec.z)
vec.x = ax > ay > az or ax > az > ay
vec.y = ay > ax > az or ay > az > ax
vec.z = az > ay > ax or az > ax > ay
if vec == Vector():
self.vector_constrain = None
else:
vc = lloc + vec
try:
if vc != self.vector_constrain[1]:
type = 'X' if vec.x else 'Y' if vec.y else 'Z' if vec.z else 'shift'
self.vector_constrain = [lloc, vc, type]
except:
type = 'X' if vec.x else 'Y' if vec.y else 'Z' if vec.z else 'shift'
self.vector_constrain = [lloc, vc, type]
if event.value == 'PRESS':
if is_making_lines and (event.ascii in CharMap.ascii or event.type in CharMap.type):
CharMap.modal(self, context, event)
elif event.type in self.constrain_keys:
self.bool_update = True
self.keyf8 = False
if self.vector_constrain and self.vector_constrain[2] == event.type:
self.vector_constrain = ()
else:
if event.shift:
if isinstance(self.geom, bmesh.types.BMEdge):
if is_making_lines:
loc = self.list_verts_co[-1]
self.vector_constrain = (loc, loc + self.geom.verts[1].co -
self.geom.verts[0].co, event.type)
else:
self.vector_constrain = [self.main_snap_obj.mat @ v.co for
v in self.geom.verts] + [event.type]
else:
if is_making_lines:
loc = self.list_verts_co[-1]
else:
loc = self.location
self.vector_constrain = [loc, loc + self.constrain_keys[event.type], event.type]
elif event.type == 'LEFTMOUSE':
if not is_making_lines and self.bm:
self.main_snap_obj = self.snap_obj
self.main_bm = self.bm
mat_inv = self.main_snap_obj.mat.inverted_safe()
point = mat_inv @ self.location
# with constraint the intersection can be in a different element of the selected one
geom2 = self.geom
if geom2:
geom2.select = False
if self.vector_constrain:
geom2 = get_closest_edge(self.main_bm, point, .001)
self.vector_constrain = None
self.list_verts_co = draw_line(self, geom2, point)
elif event.type == 'F8':
self.vector_constrain = None
self.keyf8 = self.keyf8 is False
elif event.value == 'RELEASE':
if event.type in {'RET', 'NUMPAD_ENTER'}:
if self.length_entered != "" and self.list_verts_co:
try:
text_value = bpy.utils.units.to_value(self.unit_system, 'LENGTH', self.length_entered)
vector = (self.location - self.list_verts_co[-1]).normalized()
location = (self.list_verts_co[-1] + (vector * text_value))
mat_inv = self.main_snap_obj.mat.inverted_safe()
self.list_verts_co = draw_line(self, self.geom, mat_inv @ location)
self.length_entered = ""
self.vector_constrain = None
except: # ValueError:
self.report({'INFO'}, "Operation not supported yet")
if not self.wait_for_input:
self._exit(context)
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
if not self.wait_for_input or not is_making_lines or event.type == 'ESC':
self._exit(context)
return {'FINISHED'}
else:
snap_utilities.cache.snp_obj = None # hack for snap edge elemens update
self.vector_constrain = None
self.list_edges = []
self.list_verts = []
self.list_verts_co = []
a = ""
if is_making_lines:
if self.length_entered:
pos = self.line_pos
a = 'length: ' + self.length_entered[:pos] + '|' + self.length_entered[pos:]
else:
length = self.len
length = convert_distance(length, self.uinfo)
a = 'length: ' + length
context.area.header_text_set(text = "hit: %.3f %.3f %.3f %s" % (*self.location, a))
if True or is_making_lines:
return {'RUNNING_MODAL'}
return {'PASS_THROUGH'}
def draw_callback_px(self):
if self.bm:
self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
self.draw_cache.draw(self.type, self.location, self.list_verts_co, self.vector_constrain, self.prevloc)
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
# print('name', __name__, __package__)
#Store current state
self.select_mode = context.tool_settings.mesh_select_mode[:]
self.show_face_center = context.space_data.overlay.show_face_center
#Modify the current state
bpy.ops.mesh.select_all(action='DESELECT')
context.tool_settings.mesh_select_mode = (True, False, True)
context.space_data.overlay.show_face_center = True
#Store values from 3d view context
self.rv3d = context.region_data
self.rotMat = self.rv3d.view_matrix.copy()
# self.obj_glmatrix = bgl.Buffer(bgl.GL_FLOAT, [4, 4], self.obj_matrix.transposed())
#Init event variables
self.keyf8 = False
self.snap_face = True
self.snap_widget = g_snap_widget[0]
if self.snap_widget is not None:
self.draw_cache = self.snap_widget.draw_cache
self.sctx = self.snap_widget.sctx
preferences = self.snap_widget.preferences
else:
preferences = context.user_preferences.addons[__package__].preferences
#Init DrawCache
self.draw_cache = SnapDrawn(
preferences.out_color,
preferences.face_color,
preferences.edge_color,
preferences.vert_color,
preferences.center_color,
preferences.perpendicular_color,
preferences.constrain_shift_color,
tuple(context.user_preferences.themes[0].user_interface.axis_x) + (1.0,),
tuple(context.user_preferences.themes[0].user_interface.axis_y) + (1.0,),
tuple(context.user_preferences.themes[0].user_interface.axis_z) + (1.0,)
)
#Init Snap Context
from .snap_context_l import SnapContext
self.sctx = SnapContext(context.region, context.space_data)
self.sctx.set_pixel_dist(12)
self.sctx.use_clip_planes(True)
if preferences.outer_verts:
for base in context.visible_bases:
self.sctx.add_obj(base.object, base.object.matrix_world)
self.sctx.set_snap_mode(True, True, self.snap_face)
#Configure the unit of measure
self.unit_system = context.scene.unit_settings.system
scale = context.scene.unit_settings.scale_length
separate_units = context.scene.unit_settings.use_separate
self.uinfo = get_units_info(scale, self.unit_system, separate_units)
scale /= context.space_data.overlay.grid_scale * preferences.relative_scale
self.rd = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(1 / scale))
self.intersect = preferences.intersect
self.create_face = preferences.create_face
self.outer_verts = preferences.outer_verts
self.snap_to_grid = preferences.increments_grid
self.incremental = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(preferences.incremental))
if self.snap_widget:
self.geom = self.snap_widget.geom
self.type = self.snap_widget.type
self.location = self.snap_widget.loc
if self.snap_widget.snap_obj:
context.view_layer.objects.active = self.snap_widget.snap_obj.data[0]
else:
#init these variables to avoid errors
self.geom = None
self.type = 'OUT'
self.location = Vector()
self.prevloc = Vector()
self.list_verts = []
self.list_edges = []
self.list_verts_co = []
self.bool_update = False
self.vector_constrain = ()
self.len = 0
self.length_entered = ""
self.line_pos = 0
self.navigation_ops = SnapNavigation(context, True)
active_object = context.active_object
#Create a new object
if active_object is None or active_object.type != 'MESH':
mesh = bpy.data.meshes.new("")
active_object = bpy.data.objects.new("", mesh)
context.scene.objects.link(obj)
context.scene.objects.active = active_object
else:
mesh = active_object.data
self.main_snap_obj = self.snap_obj = self.sctx._get_snap_obj_by_obj(active_object)
self.main_bm = self.bm = bmesh.from_edit_mesh(mesh) #remove at end
#modals
if not self.wait_for_input:
self.modal(context, event)
self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (), 'WINDOW', 'POST_VIEW')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "Active space must be a View3d")
return {'CANCELLED'}
def register():
bpy.utils.register_class(SnapUtilitiesLine)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,113 @@
### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
from bpy.props import (
EnumProperty,
StringProperty,
BoolProperty,
IntProperty,
FloatVectorProperty,
FloatProperty,
)
class SnapUtilitiesLinePreferences(bpy.types.AddonPreferences):
# this must match the addon name, use '__package__'
# when defining this in a submodule of a python package.
bl_idname = __package__
intersect: BoolProperty(
name="Intersect",
description="intersects created line with the existing edges, even if the lines do not intersect",
default=True)
create_face: BoolProperty(
name="Create faces",
description="Create faces defined by enclosed edges",
default=False)
outer_verts: BoolProperty(
name="Snap to outer vertices",
description="The vertices of the objects are not activated also snapped",
default=True)
increments_grid: BoolProperty(
name="Increments of Grid",
description="Snap to increments of grid",
default=False)
incremental: FloatProperty(
name="Incremental",
description="Snap in defined increments",
default=0,
min=0,
step=1,
precision=3)
relative_scale: FloatProperty(
name="Relative Scale",
description="Value that divides the global scale",
default=1,
min=0,
step=1,
precision=3)
out_color: FloatVectorProperty(name="OUT", default=(0.0, 0.0, 0.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
face_color: FloatVectorProperty(name="FACE", default=(1.0, 0.8, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
edge_color: FloatVectorProperty(name="EDGE", default=(0.0, 0.8, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
vert_color: FloatVectorProperty(name="VERT", default=(1.0, 0.5, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
center_color: FloatVectorProperty(name="CENTER", default=(1.0, 0.0, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
perpendicular_color: FloatVectorProperty(name="PERPENDICULAR", default=(0.1, 0.5, 0.5, 1.0), size=4, subtype="COLOR", min=0, max=1)
constrain_shift_color: FloatVectorProperty(name="SHIFT CONSTRAIN", default=(0.8, 0.5, 0.4, 1.0), size=4, subtype="COLOR", min=0, max=1)
def draw(self, context):
layout = self.layout
layout.label(text="Snap Colors:")
split = layout.split()
col = split.column()
col.prop(self, "out_color")
col.prop(self, "constrain_shift_color")
col = split.column()
col.prop(self, "face_color")
col = split.column()
col.prop(self, "edge_color")
col = split.column()
col.prop(self, "vert_color")
col = split.column()
col.prop(self, "center_color")
col = split.column()
col.prop(self, "perpendicular_color")
row = layout.row()
col = row.column()
#col.label(text="Snap Items:")
col.prop(self, "incremental")
col.prop(self, "increments_grid")
if self.increments_grid:
col.prop(self, "relative_scale")
col.prop(self, "outer_verts")
row.separator()
col = row.column()
col.label(text="Line Tool:")
col.prop(self, "intersect")
col.prop(self, "create_face")

View File

@ -52,6 +52,100 @@ class _SnapObjectData():
self.mat = omat
class _SnapOffscreen():
bound = None
def __init__(self, width, height):
import ctypes
self.freed = False
self.is_bound = False
self.width = width
self.height = height
self.fbo = bgl.Buffer(bgl.GL_INT, 1)
self.buf_color = bgl.Buffer(bgl.GL_INT, 1)
self.buf_depth = bgl.Buffer(bgl.GL_INT, 1)
self.cur_fbo = bgl.Buffer(bgl.GL_INT, 1)
self.cur_viewport = bgl.Buffer(bgl.GL_INT, 4)
bgl.glGenRenderbuffers(1, self.buf_depth)
bgl.glBindRenderbuffer(bgl.GL_RENDERBUFFER, self.buf_depth[0])
bgl.glRenderbufferStorage(bgl.GL_RENDERBUFFER, bgl.GL_DEPTH_COMPONENT, width, height)
bgl.glGenTextures(1, self.buf_color)
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.buf_color[0])
NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0))
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, width, height, 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL)
del NULL
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST)
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST)
bgl.glGetIntegerv(bgl.GL_FRAMEBUFFER_BINDING, self.cur_fbo)
bgl.glGenFramebuffers(1, self.fbo)
bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.fbo[0])
bgl.glFramebufferRenderbuffer(bgl.GL_FRAMEBUFFER, bgl.GL_DEPTH_ATTACHMENT, bgl.GL_RENDERBUFFER, self.buf_depth[0])
bgl.glFramebufferTexture(bgl.GL_FRAMEBUFFER, bgl.GL_COLOR_ATTACHMENT0, self.buf_color[0], 0)
bgl.glDrawBuffers(1, bgl.Buffer(bgl.GL_INT, 1, [bgl.GL_COLOR_ATTACHMENT0]))
status = bgl.glCheckFramebufferStatus(bgl.GL_FRAMEBUFFER)
if status != bgl.GL_FRAMEBUFFER_COMPLETE:
print("Framebuffer Invalid", status)
bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.cur_fbo[0])
def bind(self):
if self is not _SnapOffscreen.bound:
if _SnapOffscreen.bound is None:
bgl.glGetIntegerv(bgl.GL_FRAMEBUFFER_BINDING, self.cur_fbo)
bgl.glGetIntegerv(bgl.GL_VIEWPORT, self.cur_viewport)
bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.fbo[0])
bgl.glViewport(0, 0, self.width, self.height)
_SnapOffscreen.bound = self
def unbind(self):
if self is _SnapOffscreen.bound:
bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.cur_fbo[0])
bgl.glViewport(*self.cur_viewport)
_SnapOffscreen.bound = None
def clear(self):
is_bound = self is _SnapOffscreen.bound
if not is_bound:
self.bind()
bgl.glColorMask(bgl.GL_TRUE, bgl.GL_TRUE, bgl.GL_TRUE, bgl.GL_TRUE)
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
bgl.glDepthMask(bgl.GL_TRUE)
bgl.glClearDepth(1.0);
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_DEPTH_BUFFER_BIT)
if not is_bound:
self.unbind()
def __del__(self):
if not self.freed:
bgl.glDeleteFramebuffers(1, self.fbo)
bgl.glDeleteRenderbuffers(1, self.buf_depth)
bgl.glDeleteTextures(1, self.buf_color)
del self.fbo
del self.buf_color
del self.buf_depth
del self.cur_fbo
del self.cur_viewport
def free(self):
self.__del__()
self.freed = True
class SnapContext():
"""
Initializes the snap context with the region and space where the snap objects will be added.
@ -66,8 +160,8 @@ class SnapContext():
"""
def __init__(self, region, space):
import gpu
import ctypes
#print('Render:', bgl.glGetString(bgl.GL_RENDERER))
#print('OpenGL Version:', bgl.glGetString(bgl.GL_VERSION))
self.freed = False
self.snap_objects = []
@ -87,75 +181,74 @@ class SnapContext():
self.set_pixel_dist(12)
self._offscreen = gpu.offscreen.new(self.region.width, self.region.height)
self._texture = self._offscreen.color_texture
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self._texture)
NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0))
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, self.region.width, self.region.height, 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL)
del NULL
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST)
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST)
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
self._offscreen = _SnapOffscreen(self.region.width, self.region.height)
self.winsize = Vector((self._offscreen.width, self._offscreen.height))
self._offscreen.clear()
## PRIVATE ##
def _get_snap_obj_by_index(self, index):
for snap_obj in self.snap_objects[:self.drawn_count]:
data = snap_obj.data[1]
if index < data.first_index + data.get_tot_elems():
return snap_obj
if index:
for snap_obj in self.snap_objects[:self.drawn_count]:
data = snap_obj.data[1]
if index < data.first_index + data.get_tot_elems():
return snap_obj
return None
def _get_nearest_index(self):
r_snap_obj = None
r_value = 0
loc = [self._dist_px, self._dist_px]
d = 1
m = self.threshold
max = 2 * m - 1
offset = 1
last_snap_obj = None
r_value = 0
while m < max:
max_val = 2 * m - 1
last_value = -1
find_next_index = self._snap_mode & FACE and self._snap_mode & (VERT | EDGE)
while m < max_val:
for i in range(2):
while 2 * loc[i] * d < m:
value = int(self._snap_buffer[loc[0]][loc[1]])
loc[i] += d
if value >= offset:
if value != last_value:
r_value = value
snap_obj = self._get_snap_obj_by_index(r_value)
if self._snap_mode & FACE and self._snap_mode & (VERT | EDGE) and last_snap_obj != snap_obj:
data = snap_obj.data[1]
offset = data.first_index + data.num_tris
last_snap_obj = snap_obj
continue
return snap_obj, r_value
if find_next_index:
last_value = value
r_snap_obj = self._get_snap_obj_by_index(value)
if (r_snap_obj is None) or value < (r_snap_obj.data[1].first_index + len(r_snap_obj.data[1].tri_verts)):
continue
find_next_index = False
elif (r_snap_obj is None) or\
(value < r_snap_obj.data[1].first_index) or\
(value >= (r_snap_obj.data[1].first_index + r_snap_obj.data[1].get_tot_elems())):
r_snap_obj = self._get_snap_obj_by_index(value)
return r_snap_obj, r_value
d = -d
m += 4 * self._dist_px * d + 1
return last_snap_obj, r_value
return r_snap_obj, r_value
def _get_loc(self, snap_obj, index):
index -= snap_obj.data[1].first_index
gpu_data = snap_obj.data[1]
if gpu_data.draw_tris:
if index < snap_obj.data[1].num_tris:
num_tris = len(snap_obj.data[1].tri_verts)
if index < num_tris:
tri_verts = gpu_data.get_tri_verts(index)
tri_co = [snap_obj.mat * Vector(v) for v in gpu_data.get_tri_co(index)]
tri_co = [snap_obj.mat @ Vector(v) for v in gpu_data.get_tri_co(index)]
nor = (tri_co[1] - tri_co[0]).cross(tri_co[2] - tri_co[0])
return _Internal.intersect_line_plane(self.last_ray[1], self.last_ray[1] + self.last_ray[0], tri_co[0], nor), tri_verts
return _Internal.intersect_line_plane(self.last_ray[1], self.last_ray[1] + self.last_ray[0], tri_co[0], nor), tri_verts, tri_co
index -= gpu_data.num_tris
index -= num_tris
if gpu_data.draw_edges:
if index < snap_obj.data[1].num_edges:
num_edges = len(snap_obj.data[1].edge_verts)
if index < num_edges:
edge_verts = gpu_data.get_edge_verts(index)
edge_co = [snap_obj.mat * Vector(v) for v in gpu_data.get_edge_co(index)]
edge_co = [snap_obj.mat @ Vector(v) for v in gpu_data.get_edge_co(index)]
fac = _Internal.intersect_ray_segment_fac(*edge_co, *self.last_ray)
if (self._snap_mode) & VERT and (fac < 0.25 or fac > 0.75):
@ -163,7 +256,7 @@ class SnapContext():
proj_co = _Internal.project_co_v3(self, co)
dist = self.mval - proj_co
if abs(dist.x) < self._dist_px and abs(dist.y) < self._dist_px:
return co, (edge_verts[0] if fac < 0.5 else edge_verts[1],)
return co, (edge_verts[0] if fac < 0.5 else edge_verts[1],), co
if fac <= 0.0:
co = edge_co[0]
@ -172,15 +265,16 @@ class SnapContext():
else:
co = edge_co[0] + fac * (edge_co[1] - edge_co[0])
return co, edge_verts
return co, edge_verts, edge_co
index -= gpu_data.num_edges
index -= num_edges
if gpu_data.draw_verts:
if index < snap_obj.data[1].num_verts:
return snap_obj.mat * Vector(gpu_data.get_loosevert_co(index)), (gpu_data.get_loosevert_index(index),)
if index < len(snap_obj.data[1].looseverts):
co = snap_obj.mat @ Vector(gpu_data.get_loosevert_co(index))
return co, (gpu_data.get_loosevert_index(index),), co
return None, None
return None, None, None
def _get_snap_obj_by_obj(self, obj):
@ -203,17 +297,27 @@ class SnapContext():
def update_all(self):
self.drawn_count = 0
self._offset_cur = 1
self._offscreen.clear()
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_DEPTH_BUFFER_BIT)
def update_drawn_snap_object(self, snap_obj):
def tag_update_drawn_snap_object(self, snap_obj):
if len(snap_obj.data) > 1:
del snap_obj.data[1:]
#self.update_all()
# Update on next snap_get call #
self.proj_mat = None
def update_drawn_snap_object(self, snap_obj):
if len(snap_obj.data) > 1:
_Internal.gpu_Indices_enable_state()
from .mesh_drawing import GPU_Indices_Mesh
snap_vert = self._snap_mode & VERT != 0
snap_edge = self._snap_mode & EDGE != 0
snap_face = self._snap_mode & FACE != 0
snap_obj.data[1] = GPU_Indices_Mesh(snap_obj.data[0], snap_face, snap_edge, snap_vert)
_Internal.gpu_Indices_restore_state()
def use_clip_planes(self, value):
_Internal.gpu_Indices_use_clip_planes(self.rv3d, value)
@ -221,7 +325,7 @@ class SnapContext():
self._dist_px = int(dist_px)
self._dist_px_sq = self._dist_px ** 2
self.threshold = 2 * self._dist_px + 1
self._snap_buffer = bgl.Buffer(bgl.GL_FLOAT, (self.threshold, self.threshold))
self._snap_buffer = bgl.Buffer(bgl.GL_INT, (self.threshold, self.threshold))
def set_snap_mode(self, snap_to_vert, snap_to_edge, snap_to_face):
snap_mode = 0
@ -250,8 +354,8 @@ class SnapContext():
self.last_ray = _Internal.region_2d_to_orig_and_view_vector(self.region, self.rv3d, mval)
return self.last_ray
def snap_get(self, mval):
ret = None, None
def snap_get(self, mval, main_snap_obj = None):
ret = None, None, None
self.mval[:] = mval
snap_vert = self._snap_mode & VERT != 0
snap_edge = self._snap_mode & EDGE != 0
@ -274,15 +378,20 @@ class SnapContext():
ray_dir, ray_orig = self.get_ray(mval)
for i, snap_obj in enumerate(self.snap_objects[self.drawn_count:], self.drawn_count):
obj = snap_obj.data[0]
bbmin = Vector(obj.bound_box[0])
bbmax = Vector(obj.bound_box[6])
try:
bbmin = Vector(obj.bound_box[0])
bbmax = Vector(obj.bound_box[6])
except ReferenceError:
self.snap_objects.remove(snap_obj)
continue
if bbmin != bbmax:
MVP = proj_mat * snap_obj.mat
MVP = proj_mat @ snap_obj.mat
mat_inv = snap_obj.mat.inverted()
ray_orig_local = mat_inv * ray_orig
ray_dir_local = mat_inv.to_3x3() * ray_dir
in_threshold = _Internal.intersect_boundbox_threshold(self, MVP, ray_orig_local, ray_dir_local, bbmin, bbmax)
ray_orig_local = mat_inv @ ray_orig
ray_dir_local = mat_inv.to_3x3() @ ray_dir
in_threshold = _Internal.intersect_boundbox_threshold(
self, MVP, ray_orig_local, ray_dir_local, bbmin, bbmax)
else:
proj_co = _Internal.project_co_v3(self, snap_obj.mat.translation)
dist = self.mval - proj_co
@ -295,27 +404,38 @@ class SnapContext():
snap_obj.data.append(GPU_Indices_Mesh(obj, snap_face, snap_edge, snap_vert))
snap_obj.data[1].set_draw_mode(snap_face, snap_edge, snap_vert)
snap_obj.data[1].set_ModelViewMatrix(snap_obj.mat)
snap_obj.data[1].Draw(self._offset_cur)
if snap_obj == main_snap_obj:
snap_obj.data[1].Draw(self._offset_cur, -0.0001)
else:
snap_obj.data[1].Draw(self._offset_cur)
self._offset_cur += snap_obj.data[1].get_tot_elems()
self.snap_objects[self.drawn_count], self.snap_objects[i] = self.snap_objects[i], self.snap_objects[self.drawn_count]
tmp = self.snap_objects[self.drawn_count]
self.snap_objects[self.drawn_count] = self.snap_objects[i]
self.snap_objects[i] = tmp
self.drawn_count += 1
bgl.glReadBuffer(bgl.GL_COLOR_ATTACHMENT0)
bgl.glReadPixels(
int(self.mval[0]) - self._dist_px, int(self.mval[1]) - self._dist_px,
self.threshold, self.threshold, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, self._snap_buffer)
bgl.glReadBuffer(bgl.GL_BACK)
#bgl.glReadBuffer(bgl.GL_BACK)
#import numpy as np
#a = np.array(self._snap_buffer)
#print(a)
snap_obj, index = self._get_nearest_index()
#print(index)
#print("index:", index)
if snap_obj:
ret = self._get_loc(snap_obj, index)
bgl.glDisable(bgl.GL_DEPTH_TEST)
self._offscreen.unbind()
_Internal.gpu_Indices_restore_state()
return snap_obj, ret[0], ret[1]
return (snap_obj, *ret)
def free(self):
self.__del__()

View File

@ -0,0 +1,412 @@
# ##### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bgl
import bmesh
import numpy as np
from mathutils import Matrix
import gpu
_Hash = {}
def load_shader(shadername):
from os import path
with open(path.join(path.dirname(__file__), 'shaders', shadername), 'r') as f:
return f.read()
def get_mesh_vert_co_array(me):
tot_vco = len(me.vertices)
if tot_vco:
verts_co = np.empty(len(me.vertices) * 3, 'f4')
me.vertices.foreach_get("co", verts_co)
verts_co.shape = (-1, 3)
return verts_co
return None
def get_bmesh_vert_co_array(bm):
tot_vco = len(bm.verts)
if tot_vco:
return np.array([v.co for v in bm.verts], 'f4')
return None
def get_mesh_tri_verts_array(me):
me.calc_loop_triangles()
len_triangles = len(me.loop_triangles)
if len_triangles:
tris = np.empty(len_triangles * 3, 'i4')
me.loop_triangles.foreach_get("vertices", tris)
tris.shape = (-1, 3)
return tris
return None
def get_bmesh_tri_verts_array(bm):
l_tri_layer = bm.faces.layers.int.get("l_tri")
if l_tri_layer is None:
l_tri_layer = bm.faces.layers.int.new("l_tri")
ltris = bm.calc_loop_triangles()
tris = np.empty((len(ltris), 3), 'i4')
i = 0
last_face = bm.faces[-1]
for ltri in ltris:
face = ltri[0].face
if not face.hide:
tris[i] = ltri[0].vert.index, ltri[1].vert.index, ltri[2].vert.index
if last_face != face:
last_face = face
face[l_tri_layer] = i
i += 1
if i:
tris.resize((i, 3), refcheck=False)
return tris
return None
def get_mesh_edge_verts_array(me):
tot_edges = len(me.edges)
if tot_edges:
edge_verts = np.empty(tot_edges * 2, 'i4')
me.edges.foreach_get("vertices", edge_verts)
edge_verts.shape = tot_edges, 2
return edge_verts
return None
def get_bmesh_edge_verts_array(bm):
bm.edges.ensure_lookup_table()
edges = [[e.verts[0].index, e.verts[1].index] for e in bm.edges if not e.hide]
if edges:
return np.array(edges, 'i4')
return None
def get_mesh_loosevert_array(me, edges):
verts = np.arange(len(me.vertices))
mask = np.in1d(verts, edges, invert=True)
verts = verts[mask]
if len(verts):
return verts
return None
def get_bmesh_loosevert_array(bm):
looseverts = [v.index for v in bm.verts if not (v.link_edges or v.hide)]
if looseverts:
return np.array(looseverts, 'i4')
return None
class _Mesh_Arrays():
def __init__(self, obj, create_tris, create_edges, create_looseverts):
self.tri_verts = self.edge_verts = self.looseverts = ()
if obj.type == 'MESH':
me = obj.data
if me.is_editmode:
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
self.verts_co = get_bmesh_vert_co_array(bm)
if create_tris:
self.tri_verts = get_bmesh_tri_verts_array(bm)
if create_edges:
self.edge_verts = get_bmesh_edge_verts_array(bm)
if create_looseverts:
self.looseverts = get_bmesh_loosevert_array(bm)
del bm
else:
import bpy
self.verts_co = get_mesh_vert_co_array(me)
if create_tris:
self.tri_verts = get_mesh_tri_verts_array(me)
if create_edges:
self.edge_verts = get_mesh_edge_verts_array(me)
if create_looseverts:
edge_verts = self.edge_verts
if edge_verts is None:
edge_verts = get_mesh_edge_verts_array(me)
self.looseverts = get_mesh_loosevert_array(me, edge_verts)
del edge_verts
else: #TODO
self.verts_co = np.zeros((1,3), 'f4')
self.looseverts = np.zeros(1, 'i4')
def __del__(self):
del self.tri_verts, self.edge_verts, self.looseverts
del self.verts_co
class GPU_Indices_Mesh():
__slots__ = (
"obj",
"draw_tris",
"draw_edges",
"draw_verts",
"batch_tris",
"batch_edges",
"batch_lverts",
"verts_co",
"tri_verts",
"edge_verts",
"looseverts",
"first_index",
"users"
)
shader = None
@classmethod
def end_opengl(cls):
del cls.shader
del cls.P
del cls
@staticmethod
def init_opengl():
cls = GPU_Indices_Mesh
# OpenGL was already initialized, nothing to do here.
if cls.shader is not None:
return
import atexit
# Make sure we only registered the callback once.
atexit.unregister(cls.end_opengl)
atexit.register(cls.end_opengl)
cls.shader = gpu.types.GPUShader(
load_shader("ID_color_vert.glsl"),
load_shader("ID_color_frag.glsl"),
)
#cls.unif_use_clip_planes = cls.shader.uniform_from_name('use_clip_planes')
#cls.unif_clip_plane = cls.shader.uniform_from_name('clip_plane')
cls.unif_offset = cls.shader.uniform_from_name('offset')
cls.P = Matrix()
@staticmethod
def set_ModelViewMatrix(MV):
gpu.matrix.load_matrix(MV)
def __init__(self, obj, draw_tris, draw_edges, draw_verts):
self.obj = obj
if obj.data in _Hash:
src = _Hash[obj.data]
dst = self
dst.draw_tris = src.draw_tris
dst.draw_edges = src.draw_edges
dst.draw_verts = src.draw_verts
dst.batch_tris = src.batch_tris
dst.batch_edges = src.batch_edges
dst.batch_lverts = src.batch_lverts
dst.verts_co = src.verts_co
dst.tri_verts = src.tri_verts
dst.edge_verts = src.edge_verts
dst.looseverts = src.looseverts
dst.users = src.users
dst.users.append(self)
update = obj.type == 'MESH' and obj.data.is_editmode
else:
_Hash[obj.data] = self
self.users = [self]
update = True;
if update:
self.draw_tris = draw_tris
self.draw_edges = draw_edges
self.draw_verts = draw_verts
GPU_Indices_Mesh.init_opengl()
## Init Array ##
mesh_arrays = _Mesh_Arrays(obj, draw_tris, draw_edges, draw_verts)
if mesh_arrays.verts_co is None:
self.draw_tris = False
self.draw_edges = False
self.draw_verts = False
self.tri_verts = None
self.edge_verts = None
self.looseverts = None
return
## Create VBO for vertices ##
self.verts_co = mesh_arrays.verts_co
self.tri_verts = mesh_arrays.tri_verts
self.edge_verts = mesh_arrays.edge_verts
self.looseverts = mesh_arrays.looseverts
del mesh_arrays
format = gpu.types.GPUVertFormat()
format.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
vbo = gpu.types.GPUVertBuf(format, len = len(self.verts_co))
vbo.attr_fill(0, data = self.verts_co)
## Create Batch for Tris ##
if self.tri_verts is not None:
ebo = gpu.types.GPUIndexBuf(type = "TRIS", seq = self.tri_verts)
self.batch_tris = gpu.types.GPUBatch(type = "TRIS", buf = vbo, elem = ebo)
self.batch_tris.program_set(self.shader)
else:
self.draw_tris = False
self.batch_tris = None
## Create Batch for Edges ##
if self.edge_verts is not None:
ebo = gpu.types.GPUIndexBuf(type = "LINES", seq = self.edge_verts)
self.batch_edges = gpu.types.GPUBatch(type = "LINES", buf = vbo, elem = ebo)
self.batch_edges.program_set(self.shader)
else:
self.draw_edges = False
self.batch_edges = None
## Create Batch for Loose Verts ##
if self.looseverts is not None:
ebo = gpu.types.GPUIndexBuf(type = "POINTS", seq = self.looseverts)
self.batch_lverts = gpu.types.GPUBatch(type = "POINTS", buf = vbo, elem = ebo)
self.batch_lverts.program_set(self.shader)
else:
self.draw_verts = False
self.batch_lverts = None
def get_tot_elems(self):
tot = 0
if self.draw_tris:
tot += len(self.tri_verts)
if self.draw_edges:
tot += len(self.edge_verts)
if self.draw_verts:
tot += len(self.looseverts)
return tot
def set_draw_mode(self, draw_tris, draw_edges, draw_verts):
self.draw_tris = draw_tris and self.tri_verts is not None
self.draw_edges = draw_edges and self.edge_verts is not None
self.draw_verts = draw_verts and self.looseverts is not None
def Draw(self, index_offset, depth_offset = -0.00005):
self.first_index = index_offset
if self.draw_tris:
self.shader.uniform_int("offset", (index_offset,))
self.batch_tris.draw(self.shader)
index_offset += len(self.tri_verts)
bgl.glDepthRange(depth_offset, 1 + depth_offset)
if self.draw_edges:
self.shader.uniform_int("offset", (index_offset,))
#bgl.glLineWidth(3.0)
self.batch_edges.draw(self.shader)
#bgl.glLineWidth(1.0)
index_offset += len(self.edge_verts)
if self.draw_verts:
self.shader.uniform_int("offset", (index_offset,))
self.batch_lverts.draw(self.shader)
bgl.glDepthRange(0.0, 1.0)
def get_tri_co(self, index):
return self.verts_co[self.tri_verts[index]]
def get_edge_co(self, index):
return self.verts_co[self.edge_verts[index]]
def get_loosevert_co(self, index):
return self.verts_co[self.looseverts[index]]
def get_loop_tri_co_by_bmface(self, bm, bmface):
l_tri_layer = bm.faces.layers.int["l_tri"]
tri = bmface[l_tri_layer]
return self.verts_co[self.tri_verts[tri : tri + len(bmface.verts) - 2]]
def get_tri_verts(self, index):
return self.tri_verts[index]
def get_edge_verts(self, index):
return self.edge_verts[index]
def get_loosevert_index(self, index):
return self.looseverts[index]
def __del__(self):
if len(self.users) == 1:
self.free_gl()
_Hash.pop(obj.data)
self.user.remove(self)
#print('mesh_del', self.obj.name)
def gpu_Indices_enable_state():
GPU_Indices_Mesh.init_opengl()
gpu.matrix.push()
gpu.matrix.push_projection()
gpu.matrix.load_projection_matrix(GPU_Indices_Mesh.P)
GPU_Indices_Mesh.shader.bind()
def gpu_Indices_restore_state():
gpu.matrix.pop()
gpu.matrix.pop_projection()
def gpu_Indices_use_clip_planes(rv3d, value):
pass #TODO
#if rv3d.use_clip_planes:
#planes = bgl.Buffer(bgl.GL_FLOAT, (6, 4), rv3d.clip_planes)
#_store_current_shader_state(PreviousGLState)
#GPU_Indices_Mesh.init_opengl()
#bgl.glUseProgram(GPU_Indices_Mesh.shader.program)
#bgl.glUniform1i(GPU_Indices_Mesh.unif_use_clip_planes, value)
#bgl.glUniform4fv(GPU_Indices_Mesh.unif_clip_plane, 4, planes)
#_restore_shader_state(PreviousGLState)
def gpu_Indices_set_ProjectionMatrix(P):
gpu.matrix.load_projection_matrix(P)
GPU_Indices_Mesh.P[:] = P

View File

@ -1,14 +1,15 @@
#version 120
uniform int offset;
#ifdef USE_CLIP_PLANES
uniform bool use_clip_planes;
varying vec4 clip_distance;
in vec4 clip_distance;
#endif
uniform float offset;
varying float primitive_id_var;
out uint FragColor;
void main()
{
#ifdef USE_CLIP_PLANES
if (use_clip_planes &&
((clip_distance[0] < 0) ||
(clip_distance[1] < 0) ||
@ -17,6 +18,7 @@ void main()
{
discard;
}
#endif
gl_FragColor = vec4(offset + primitive_id_var, 0, 0, 0);
FragColor = uint(gl_PrimitiveID + offset);
}

View File

@ -0,0 +1,25 @@
uniform mat4 ModelViewProjectionMatrix;
#ifdef USE_CLIP_PLANES
uniform mat4 ModelViewMatrix;
uniform bool use_clip_planes;
uniform vec4 clip_plane[4];
out vec4 clip_distance;
#endif
in vec3 pos;
void main()
{
#ifdef USE_CLIP_PLANES
if (use_clip_planes) {
vec4 g_pos = ModelViewMatrix * vec4(pos, 1.0);
for (int i = 0; i != 4; i++) {
clip_distance[i] = dot(clip_plane[i], g_pos);
}
}
#endif
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
}

View File

@ -26,8 +26,8 @@ def depth_get(co, ray_start, ray_dir):
def region_2d_to_orig_and_view_vector(region, rv3d, coord):
viewinv = rv3d.view_matrix.inverted()
persinv = rv3d.perspective_matrix.inverted()
viewinv = rv3d.view_matrix.inverted_safe()
persinv = rv3d.perspective_matrix.inverted_safe()
dx = (2.0 * coord[0] / region.width) - 1.0
dy = (2.0 * coord[1] / region.height) - 1.0
@ -39,7 +39,7 @@ def region_2d_to_orig_and_view_vector(region, rv3d, coord):
w = out.dot(persinv[3].xyz) + persinv[3][3]
view_vector = ((persinv * out) / w) - origin_start
view_vector = ((persinv @ out) / w) - origin_start
else:
view_vector = -viewinv.col[2].xyz
@ -52,8 +52,11 @@ def region_2d_to_orig_and_view_vector(region, rv3d, coord):
def project_co_v3(sctx, co):
proj_co = sctx.proj_mat * co.to_4d()
proj_co.xy /= proj_co.w
proj_co = sctx.proj_mat @ co.to_4d()
try:
proj_co.xy /= proj_co.w
except Exception as e:
print(e)
win_half = sctx.winsize * 0.5
proj_co[0] = (proj_co[0] + 1.0) * win_half[0]
@ -210,3 +213,4 @@ def intersect_ray_segment_fac(v0, v1, ray_direction, ray_origin):
c = n - t
cray = c.cross(ray_direction)
return cray.dot(n) / nlen

View File

@ -1,507 +0,0 @@
# ##### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bgl
import bmesh
import numpy as np
from mathutils import Matrix
from .utils_shader import Shader
def load_shader(shadername):
from os import path
with open(path.join(path.dirname(__file__), 'resources', shadername), 'r') as f:
return f.read()
def gl_buffer_void_as_long(value):
import ctypes
a = (ctypes.c_byte * 1).from_address(value)
return bgl.Buffer(bgl.GL_BYTE, 1, a)
def get_mesh_vert_co_array(me):
tot_vco = len(me.vertices)
if tot_vco:
verts_co = np.empty(len(me.vertices) * 3, 'f4')
me.vertices.foreach_get("co", verts_co)
verts_co.shape = (-1, 3)
return verts_co
return None
def get_bmesh_vert_co_array(bm):
tot_vco = len(bm.verts)
if tot_vco:
return np.array([v.co for v in bm.verts], 'f4')
return None
def get_mesh_tri_verts_array(me):
me.calc_loop_triangles()
tris = [tri.vertices[:] for tri in me.loop_triangles]
if tris:
return np.array(tris, 'i4')
return None
def get_bmesh_tri_verts_array(bm):
ltris = bm.calc_loop_triangles()
tris = [[ltri[0].vert.index, ltri[1].vert.index, ltri[2].vert.index] for ltri in ltris if not ltri[0].face.hide]
if tris:
return np.array(tris, 'i4')
return None
def get_mesh_edge_verts_array(me):
tot_edges = len(me.edges)
if tot_edges:
edge_verts = np.empty(tot_edges * 2, 'i4')
me.edges.foreach_get("vertices", edge_verts)
edge_verts.shape = tot_edges, 2
return edge_verts
return None
def get_bmesh_edge_verts_array(bm):
bm.edges.ensure_lookup_table()
edges = [[e.verts[0].index, e.verts[1].index] for e in bm.edges if not e.hide]
if edges:
return np.array(edges, 'i4')
return None
def get_mesh_loosevert_array(me, edges):
verts = np.arange(len(me.vertices))
mask = np.in1d(verts, edges, invert=True)
verts = verts[mask]
if len(verts):
return verts
return None
def get_bmesh_loosevert_array(bm):
looseverts = [v.index for v in bm.verts if not (v.link_edges or v.hide)]
if looseverts:
return np.array(looseverts, 'i4')
return None
class _Mesh_Arrays():
def __init__(self, obj, create_tris, create_edges, create_looseverts):
self.tri_verts = self.edge_verts = self.looseverts = None
self.tris_co = self.edges_co = self.looseverts_co = None
if obj.type == 'MESH':
me = obj.data
if me.is_editmode:
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
self.verts_co = get_bmesh_vert_co_array(bm)
if create_tris:
self.tri_verts = get_bmesh_tri_verts_array(bm)
if create_edges:
self.edge_verts = get_bmesh_edge_verts_array(bm)
if create_looseverts:
self.looseverts = get_bmesh_loosevert_array(bm)
else:
self.verts_co = get_mesh_vert_co_array(me)
if create_tris:
self.tri_verts = get_mesh_tri_verts_array(me)
if create_edges:
self.edge_verts = get_mesh_edge_verts_array(me)
if create_looseverts:
edge_verts = self.edge_verts
if edge_verts is None:
edge_verts = get_mesh_edge_verts_array(me)
self.looseverts = get_mesh_loosevert_array(me, edge_verts)
del edge_verts
else: #TODO
self.verts_co = np.zeros((1,3), 'f4')
self.looseverts = np.zeros(1, 'i4')
def __del__(self):
del self.tri_verts, self.edge_verts, self.looseverts
del self.verts_co
class GPU_Indices_Mesh():
shader = None
@classmethod
def end_opengl(cls):
del cls.shader
del cls._NULL
del cls.P
del cls.MV
del cls.MVP
del cls.vert_index
del cls.tri_co
del cls.edge_co
del cls.vert_co
del cls
@classmethod
def init_opengl(cls):
# OpenGL was already initialized, nothing to do here.
if cls.shader is not None:
return
import atexit
# Make sure we only registered the callback once.
atexit.unregister(cls.end_opengl)
atexit.register(cls.end_opengl)
cls.shader = Shader(
load_shader('3D_vert.glsl'),
None,
load_shader('primitive_id_frag.glsl'),
)
cls.unif_use_clip_planes = bgl.glGetUniformLocation(cls.shader.program, 'use_clip_planes')
cls.unif_clip_plane = bgl.glGetUniformLocation(cls.shader.program, 'clip_plane')
cls._NULL = gl_buffer_void_as_long(0)
cls.unif_MVP = bgl.glGetUniformLocation(cls.shader.program, 'MVP')
cls.unif_MV = bgl.glGetUniformLocation(cls.shader.program, 'MV')
cls.unif_offset = bgl.glGetUniformLocation(cls.shader.program, 'offset')
cls.attr_pos = bgl.glGetAttribLocation(cls.shader.program, 'pos')
cls.attr_primitive_id = bgl.glGetAttribLocation(cls.shader.program, 'primitive_id')
cls.P = bgl.Buffer(bgl.GL_FLOAT, (4, 4))
cls.MV = bgl.Buffer(bgl.GL_FLOAT, (4, 4))
cls.MVP = bgl.Buffer(bgl.GL_FLOAT, (4, 4))
# returns of public API #
cls.vert_index = bgl.Buffer(bgl.GL_INT, 1)
cls.tri_co = bgl.Buffer(bgl.GL_FLOAT, (3, 3))
cls.edge_co = bgl.Buffer(bgl.GL_FLOAT, (2, 3))
cls.vert_co = bgl.Buffer(bgl.GL_FLOAT, 3)
def __init__(self, obj, draw_tris, draw_edges, draw_verts):
GPU_Indices_Mesh.init_opengl()
self.obj = obj
self.draw_tris = draw_tris
self.draw_edges = draw_edges
self.draw_verts = draw_verts
self.vbo = None
self.vbo_tris = None
self.vbo_edges = None
self.vbo_verts = None
## Create VAO ##
self.vao = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenVertexArrays(1, self.vao)
bgl.glBindVertexArray(self.vao[0])
## Init Array ##
mesh_arrays = _Mesh_Arrays(obj, draw_tris, draw_edges, draw_verts)
## Create VBO for vertices ##
if mesh_arrays.verts_co is None:
self.draw_tris = False
self.draw_edges = False
self.draw_verts = False
return
if False: # Blender 2.8
self.vbo_len = len(mesh_arrays.verts_co)
self.vbo = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo[0])
verts_co = bgl.Buffer(bgl.GL_FLOAT, mesh_arrays.verts_co.shape, mesh_arrays.verts_co)
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.vbo_len * 12, verts_co, bgl.GL_STATIC_DRAW)
## Create VBO for Tris ##
if mesh_arrays.tri_verts is not None:
self.tri_verts = mesh_arrays.tri_verts
self.num_tris = len(self.tri_verts)
np_tris_co = mesh_arrays.verts_co[mesh_arrays.tri_verts]
np_tris_co = bgl.Buffer(bgl.GL_FLOAT, np_tris_co.shape, np_tris_co)
self.vbo_tris = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_tris)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_tris * 36, np_tris_co, bgl.GL_STATIC_DRAW)
del np_tris_co
tri_indices = np.repeat(np.arange(self.num_tris, dtype = 'f4'), 3)
tri_indices = bgl.Buffer(bgl.GL_FLOAT, tri_indices.shape, tri_indices)
self.vbo_tri_indices = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_tri_indices)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tri_indices[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_tris * 12, tri_indices, bgl.GL_STATIC_DRAW)
del tri_indices
else:
self.num_tris = 0
self.draw_tris = False
## Create VBO for Edges ##
if mesh_arrays.edge_verts is not None:
self.edge_verts = mesh_arrays.edge_verts
self.num_edges = len(self.edge_verts)
np_edges_co = mesh_arrays.verts_co[mesh_arrays.edge_verts]
np_edges_co = bgl.Buffer(bgl.GL_FLOAT, np_edges_co.shape, np_edges_co)
self.vbo_edges = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_edges)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_edges * 24, np_edges_co, bgl.GL_STATIC_DRAW)
del np_edges_co
edge_indices = np.repeat(np.arange(self.num_edges, dtype = 'f4'), 2)
edge_indices = bgl.Buffer(bgl.GL_FLOAT, edge_indices.shape, edge_indices)
self.vbo_edge_indices = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_edge_indices)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edge_indices[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_edges * 8, edge_indices, bgl.GL_STATIC_DRAW)
del edge_indices
else:
self.num_edges = 0
self.draw_edges = False
## Create EBO for Loose Verts ##
if mesh_arrays.looseverts is not None:
self.looseverts = mesh_arrays.looseverts
self.num_verts = len(mesh_arrays.looseverts)
np_lverts_co = mesh_arrays.verts_co[mesh_arrays.looseverts]
np_lverts_co = bgl.Buffer(bgl.GL_FLOAT, np_lverts_co.shape, np_lverts_co)
self.vbo_verts = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_verts)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_verts * 12, np_lverts_co, bgl.GL_STATIC_DRAW)
del np_lverts_co
looseverts_indices = np.arange(self.num_verts, dtype = 'f4')
looseverts_indices = bgl.Buffer(bgl.GL_FLOAT, looseverts_indices.shape, looseverts_indices)
self.vbo_looseverts_indices = bgl.Buffer(bgl.GL_INT, 1)
bgl.glGenBuffers(1, self.vbo_looseverts_indices)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_looseverts_indices[0])
bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_verts * 4, looseverts_indices, bgl.GL_STATIC_DRAW)
del looseverts_indices
else:
self.num_verts = 0
self.draw_verts = False
del mesh_arrays
bgl.glBindVertexArray(0)
def get_tot_elems(self):
tot = 0
if self.draw_tris:
tot += self.num_tris
if self.draw_edges:
tot += self.num_edges
if self.draw_verts:
tot += self.num_verts
return tot
def set_draw_mode(self, draw_tris, draw_edges, draw_verts):
self.draw_tris = draw_tris and self.vbo_tris
self.draw_edges = draw_edges and self.vbo_edges
self.draw_verts = draw_verts and self.vbo_verts
def set_ModelViewMatrix(self, MV):
self.MV[:] = MV[:]
self.MVP[:] = Matrix(self.P) * MV
def Draw(self, index_offset):
self.first_index = index_offset
bgl.glUseProgram(self.shader.program)
bgl.glBindVertexArray(self.vao[0])
bgl.glUniformMatrix4fv(self.unif_MV, 1, bgl.GL_TRUE, self.MV)
bgl.glUniformMatrix4fv(self.unif_MVP, 1, bgl.GL_TRUE, self.MVP)
if self.draw_tris:
bgl.glUniform1f(self.unif_offset, float(index_offset)) # bgl has no glUniform1ui :\
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0])
bgl.glEnableVertexAttribArray(self.attr_pos)
bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tri_indices[0])
bgl.glEnableVertexAttribArray(self.attr_primitive_id)
bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glDrawArrays(bgl.GL_TRIANGLES, 0, self.num_tris * 3)
index_offset += self.num_tris
bgl.glDepthRange(-0.00005, 0.99995)
if self.draw_edges:
bgl.glUniform1f(self.unif_offset, float(index_offset)) #TODO: use glUniform1ui
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0])
bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glEnableVertexAttribArray(self.attr_pos)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edge_indices[0])
bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glEnableVertexAttribArray(self.attr_primitive_id)
bgl.glDrawArrays(bgl.GL_LINES, 0, self.num_edges * 2)
index_offset += self.num_edges
if self.draw_verts:
bgl.glUniform1f(self.unif_offset, float(index_offset)) #TODO: use glUniform1ui
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0])
bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glEnableVertexAttribArray(self.attr_pos)
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_looseverts_indices[0])
bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL)
bgl.glEnableVertexAttribArray(self.attr_primitive_id)
bgl.glDrawArrays(bgl.GL_POINTS, 0, self.num_verts)
bgl.glDepthRange(0.0, 1.0)
def get_tri_co(self, index):
bgl.glBindVertexArray(self.vao[0])
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0])
bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 36, 36, self.tri_co)
bgl.glBindVertexArray(0)
return self.tri_co
def get_edge_co(self, index):
bgl.glBindVertexArray(self.vao[0])
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0])
bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 24, 24, self.edge_co)
bgl.glBindVertexArray(0)
return self.edge_co
def get_loosevert_co(self, index):
bgl.glBindVertexArray(self.vao[0])
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0])
bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 12, 12, self.vert_co)
bgl.glBindVertexArray(0)
return self.vert_co
def get_tri_verts(self, index):
return self.tri_verts[index]
def get_edge_verts(self, index):
return self.edge_verts[index]
def get_loosevert_index(self, index):
return self.looseverts[index]
def __del__(self):
if self.vbo_tris:
bgl.glDeleteBuffers(1, self.vbo_tris)
bgl.glDeleteBuffers(1, self.vbo_tri_indices)
del self.tri_verts
if self.vbo_edges:
bgl.glDeleteBuffers(1, self.vbo_edges)
bgl.glDeleteBuffers(1, self.vbo_edge_indices)
del self.edge_verts
if self.vbo_verts:
bgl.glDeleteBuffers(1, self.vbo_verts)
bgl.glDeleteBuffers(1, self.vbo_looseverts_indices)
del self.looseverts
bgl.glDeleteVertexArrays(1, self.vao)
#print('mesh_del', self.obj.name)
class PreviousGLState:
buf = bgl.Buffer(bgl.GL_INT, (4, 1))
cur_program = buf[0]
cur_vao = buf[1]
cur_vbo = buf[2]
cur_ebo = buf[3]
def _store_current_shader_state(cls):
bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, cls.cur_program)
bgl.glGetIntegerv(bgl.GL_VERTEX_ARRAY_BINDING, cls.cur_vao)
bgl.glGetIntegerv(bgl.GL_ARRAY_BUFFER_BINDING, cls.cur_vbo)
bgl.glGetIntegerv(bgl.GL_ELEMENT_ARRAY_BUFFER_BINDING, cls.cur_ebo)
def _restore_shader_state(cls):
bgl.glUseProgram(cls.cur_program[0])
bgl.glBindVertexArray(cls.cur_vao[0])
bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, cls.cur_vbo[0])
bgl.glBindBuffer(bgl.GL_ELEMENT_ARRAY_BUFFER, cls.cur_ebo[0])
def gpu_Indices_enable_state():
_store_current_shader_state(PreviousGLState)
GPU_Indices_Mesh.init_opengl()
bgl.glUseProgram(GPU_Indices_Mesh.shader.program)
#bgl.glBindVertexArray(GPU_Indices_Mesh.vao[0])
def gpu_Indices_restore_state():
bgl.glBindVertexArray(0)
_restore_shader_state(PreviousGLState)
def gpu_Indices_use_clip_planes(rv3d, value):
if rv3d.use_clip_planes:
planes = bgl.Buffer(bgl.GL_FLOAT, (6, 4), rv3d.clip_planes)
_store_current_shader_state(PreviousGLState)
GPU_Indices_Mesh.init_opengl()
bgl.glUseProgram(GPU_Indices_Mesh.shader.program)
bgl.glUniform1i(GPU_Indices_Mesh.unif_use_clip_planes, value)
bgl.glUniform4fv(GPU_Indices_Mesh.unif_clip_plane, 4, planes)
_restore_shader_state(PreviousGLState)
def gpu_Indices_set_ProjectionMatrix(P):
GPU_Indices_Mesh.P[:] = P

View File

@ -1,26 +0,0 @@
#version 120
uniform bool use_clip_planes;
uniform vec4 clip_plane[4];
varying vec4 clip_distance;
uniform mat4 MV;
uniform mat4 MVP;
attribute vec3 pos;
attribute float primitive_id;
varying float primitive_id_var;
void main()
{
if (use_clip_planes) {
vec4 g_pos = MV * vec4(pos, 1.0);
for (int i = 0; i != 4; i++) {
clip_distance[i] = dot(clip_plane[i], g_pos);
}
}
primitive_id_var = primitive_id;
gl_Position = MVP * vec4(pos, 1.0);
}

View File

@ -1,85 +0,0 @@
# ##### 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 3
# 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bgl
def check_shaderError(shader, flag, isProgram, errorMessage):
success = bgl.Buffer(bgl.GL_INT, 1)
if isProgram:
bgl.glGetProgramiv(shader, flag, success)
else:
bgl.glGetShaderiv(shader, flag, success)
if success[0] == bgl.GL_FALSE:
import numpy as np
import ctypes
offset = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0))
error = bgl.Buffer(bgl.GL_BYTE, 1024)
if isProgram:
bgl.glGetProgramInfoLog(shader, 1024, offset, error)
print(errorMessage, np.bytes_(error).decode("utf-8"))
else:
bgl.glGetShaderInfoLog(shader, 1024, offset, error)
print(errorMessage, np.bytes_(error).decode("utf-8"))
del offset
raise #RuntimeError(errorMessage, bgl.glGetShaderInfoLog(shader))
def create_shader(source, shaderType):
shader = bgl.glCreateShader(shaderType)
if shader == 0:
raise RuntimeError("Error: Shader creation failed!")
bgl.glShaderSource(shader, source)
bgl.glCompileShader(shader)
check_shaderError(shader, bgl.GL_COMPILE_STATUS, False, "Error: Shader compilation failed:")
return shader
class Shader():
def __init__(self, vertexcode, geomcode, fragcode):
self.program = bgl.glCreateProgram()
self.shaders = []
if vertexcode:
self.shaders.append(create_shader(vertexcode, bgl.GL_VERTEX_SHADER))
if geomcode:
self.shaders.append(create_shader(geomcode, bgl.GL_GEOMETRY_SHADER))
if fragcode:
self.shaders.append(create_shader(fragcode, bgl.GL_FRAGMENT_SHADER))
for shad in self.shaders:
bgl.glAttachShader(self.program, shad)
bgl.glLinkProgram(self.program)
check_shaderError(self.program, bgl.GL_LINK_STATUS, True, "Error: Program linking failed:")
bgl.glValidateProgram(self.program)
check_shaderError(self.program, bgl.GL_VALIDATE_STATUS, True, "Error: Program is invalid:")
def __del__(self):
for shad in self.shaders:
bgl.glDetachShader(self.program, shad)
bgl.glDeleteShader(shad)
bgl.glDeleteProgram(self.program)
#print('shader_del')

View File

@ -193,7 +193,7 @@ class OBJECT_OT_collection_unlink(Operator):
def select_collection_objects(collection):
for ob in collection.objects:
ob.select_set('SELECT')
ob.select_set(True)
for collection_nested in collection.collections:
select_collection_objects(collection_nested)

View File

@ -1,401 +0,0 @@
# ##### 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-80 compliant>
# Script copyright (C) Campbell Barton
bl_info = {
"name": "Grease Scatter Objects",
"author": "Campbell Barton",
"version": (0, 1),
"blender": (2, 58, 0),
"location": "3D View, Add Mesh",
"description": "Scatter a group of objects onto the active mesh using "
"the grease pencil lines",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Object/Grease_Scatter",
"support": 'OFFICIAL',
"category": "Object",
}
from mathutils import Vector, Matrix, Quaternion
from random import uniform, shuffle
import bpy
def _main(self,
obj,
group,
DENSITY=1.0,
SCALE=0.6,
RAND_LOC=0.8,
RAND_ALIGN=0.75,
):
from math import radians, pi
# OFS = 0.2
SEEK = 2.0 # distance for ray to seek
BAD_NORMAL = Vector((0.0, 0.0, -1.0))
WALL_LIMIT = radians(45.0)
mats = (Matrix.Rotation(radians(-45), 3, 'X'),
Matrix.Rotation(radians(+45), 3, 'X'),
Matrix.Rotation(radians(-45), 3, 'Y'),
Matrix.Rotation(radians(+45), 3, 'Y'),
Matrix.Rotation(radians(-45), 3, 'Z'),
Matrix.Rotation(radians(+45), 3, 'Z'),
)
Z_UP = Vector((0.0, 0.0, 1.0))
Y_UP = Vector((0.0, 1.0, 0.0))
if not group:
self.report({'WARNING'}, "Group '%s' not found" % obj.name)
return
def debug_edge(v1, v2):
mesh = bpy.data.meshes.new("Retopo")
mesh.from_pydata([v1, v2], [(0.0, 1.0)], [])
scene = bpy.context.scene
mesh.update()
obj_new = bpy.data.objects.new("Torus", mesh)
scene.objects.link(obj_new)
ray = obj.ray_cast
closest_point_on_mesh = obj.closest_point_on_mesh
obj_mat = obj.matrix_world.copy()
obj_mat_inv = obj_mat.inverted()
# obj_quat = obj_mat.to_quaternion()
# obj_quat_inv = obj_mat_inv.to_quaternion()
DEBUG = False
def fix_point(p):
ok, hit, no, ind = closest_point_on_mesh(obj_mat_inv * p)
if ok:
if DEBUG:
return [p, no, None]
else:
# print("good", hit, no)
return [hit, no, None]
# worry!
print("bad!", p, BAD_NORMAL)
return [p, BAD_NORMAL, None]
def get_points(stroke):
return [fix_point(point.co) for point in stroke.points]
def get_splines(gp):
if gp.layers.active:
frame = gp.layers.active.active_frame
return [get_points(stroke) for stroke in frame.strokes]
else:
return []
def main():
scene = bpy.context.scene
obj = bpy.context.object
gp = None
if obj:
gp = obj.grease_pencil
if not gp:
gp = scene.grease_pencil
if not gp:
self.report({'WARNING'}, "No grease pencil layer found")
return
splines = get_splines(gp)
for s in splines:
for pt in s:
p = pt[0]
n = pt[1]
# print(p, n)
if n is BAD_NORMAL:
continue
# # dont self intersect
best_nor = None
#best_hit = None
best_dist = 10000000.0
pofs = p + n * 0.01
n_seek = n * SEEK
m_alt_1 = Matrix.Rotation(radians(22.5), 3, n)
m_alt_2 = Matrix.Rotation(radians(-22.5), 3, n)
for _m in mats:
for m in (_m, m_alt_1 * _m, m_alt_2 * _m):
pdir = m * n_seek
ok, hit, nor, ind = ray(pofs, pdir, best_dist)
if ok:
best_dist = (pofs - hit).length
best_nor = nor
# best_hit = hit
if best_nor:
pt[1].length = best_dist
best_nor.negate()
pt[2] = best_nor
#scene.cursor_location[:] = best_hitnyway
# bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP',
# iterations=1)
# debug_edge(p, best_hit)
# p[:] = best_hit
# Now we need to do scattering.
# first corners
hits = []
nors = []
oris = []
for s in splines:
# point, normal, n_other the closest hit normal
for p, n, n_other in s:
if n is BAD_NORMAL:
continue
if n_other:
# cast vectors twice as long as the distance
# needed just in case.
n_down = (n * -SEEK)
l = n_down.length
n_other.length = l
vantage = p + n
if DEBUG:
p[:] = vantage
# We should cast rays between n_down and n_other
#for f in (0.0, 0.2, 0.4, 0.6, 0.8, 1.0):
TOT = int(10 * DENSITY)
#for i in list(range(TOT)):
for i in list(range(TOT))[int(TOT / 1.5):]: # second half
f = i / (TOT - 1)
# focus on the center
'''
f -= 0.5
f = f*f
f += 0.5
'''
ntmp = f * n_down + (1.0 - f) * n_other
# randomize
ntmp.x += uniform(-l, l) * RAND_LOC
ntmp.y += uniform(-l, l) * RAND_LOC
ntmp.z += uniform(-l, l) * RAND_LOC
ok, hit, hit_no, ind = ray(vantage, ntmp, ntmp.length)
# print(hit, hit_no)
if ok:
if hit_no.angle(Z_UP) < WALL_LIMIT:
hits.append(hit)
nors.append(hit_no)
oris.append(n_other.cross(hit_no))
#oris.append(n_other)
if 0:
mesh = bpy.data.meshes.new("ScatterDupliFace")
mesh.from_pydata(hits, [], [])
scene = bpy.context.scene
mesh.update()
obj_new = bpy.data.objects.new("ScatterPar", mesh)
scene.objects.link(obj_new)
obj_new.layers[:] = obj.layers
# Now setup dupli-faces
obj_new.dupli_type = 'VERTS'
ob_child = bpy.data.objects["trash"]
ob_child.location = obj_new.location
ob_child.parent = obj_new
else:
def apply_faces(triples):
# first randomize the faces
shuffle(triples)
obs = group.objects[:]
tot = len(obs)
tot_div = int(len(triples) / tot)
for inst_ob in obs:
triple_sub = triples[0:tot_div]
triples[0:tot_div] = []
vv = [tuple(v) for f in triple_sub for v in f]
mesh = bpy.data.meshes.new("ScatterDupliFace")
mesh.from_pydata(vv, [], [(i * 3, i * 3 + 1, i * 3 + 2)
for i in range(len(triple_sub))])
scene = bpy.context.scene
mesh.update()
obj_new = bpy.data.objects.new("ScatterPar", mesh)
scene.objects.link(obj_new)
obj_new.layers[:] = obj.layers
# Now setup dupli-faces
obj_new.dupli_type = 'FACES'
obj_new.use_dupli_faces_scale = True
obj_new.dupli_faces_scale = 100.0
inst_ob.location = 0.0, 0.0, 0.0
inst_ob.parent = obj_new
# align the object with worldspace
obj_new.matrix_world = obj_mat
# BGE settings for testing
'''
inst_ob.game.physics_type = 'RIGID_BODY'
inst_ob.game.use_collision_bounds = True
inst_ob.game.collision_bounds = 'TRIANGLE_MESH'
inst_ob.game.collision_margin = 0.1
obj_new.select = True
'''
# build faces from vert/normals
tri = (Vector((0.0, 0.0, 0.01)),
Vector((0.0, 0.0, 0.0)),
Vector((0.0, 0.01, 0.01)))
coords = []
# face_ind = []
for i in range(len(hits)):
co = hits[i]
no = nors[i]
ori = oris[i]
quat = no.to_track_quat('X', 'Z')
# make 2 angles and blend
angle = uniform(-pi, pi)
angle_aligned = -(ori.angle(quat * Y_UP, pi))
quat = Quaternion(no,
(angle * (1.0 - RAND_ALIGN)) +
(angle_aligned * RAND_ALIGN)
).cross(quat)
f = uniform(0.1, 1.2) * SCALE
coords.append([co + (quat * (tri[0] * f)),
co + (quat * (tri[1] * f)),
co + (quat * (tri[2] * f)),
])
apply_faces(coords)
main()
from bpy.props import FloatProperty, StringProperty
class Scatter(bpy.types.Operator):
""""""
bl_idname = "object.scatter"
bl_label = "Grease Pencil Scatter"
density = FloatProperty(
name="Density",
description="Multiplier for the density of items",
default=1.0, min=0.01, max=10.0,
)
scale = FloatProperty(
name="Scale",
description="Size multiplier for duplifaces",
default=1.0, min=0.01, max=10.0,
)
rand_align = FloatProperty(
name="Random Align",
description="Randomize alignment with the walls",
default=0.75, min=0.0, max=1.0,
)
rand_loc = FloatProperty(
name="Random Loc",
description="Randomize placement",
default=0.75, min=0.0, max=1.0,
)
# XXX, should not be a string - TODO, add a way for scritps to select ID's
group = StringProperty(
name="Group",
description=("Group name to use for object placement, "
"defaults to object name when that matches a group"))
def execute(self, context):
obj = bpy.context.object
group = bpy.data.groups.get(self.group)
if not group:
self.report({'ERROR'}, "Group %r not found" % self.group)
return {'CANCELLED'}
_main(self,
obj,
group,
DENSITY=self.density,
SCALE=self.scale,
RAND_LOC=self.rand_loc,
RAND_ALIGN=self.rand_align,
)
return {'FINISHED'}
def check(self, context):
if self.group not in bpy.data.groups:
self.group = ""
return True
return False
def invoke(self, context, event):
# useful to initialize, take a guess
if not self.group and context.object.name in bpy.data.groups:
self.group = context.object.name
wm = context.window_manager
wm.invoke_props_dialog(self, width=180)
return {'RUNNING_MODAL'}
def menu_func(self, context):
self.layout.operator(Scatter.bl_idname, icon='AUTO')
def register():
bpy.utils.register_class(Scatter)
bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
def unregister():
bpy.utils.unregister_class(Scatter)
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
#if __name__ == "__main__":
# _main()

View File

@ -158,12 +158,8 @@ def bmesh_check_thick_object(obj, thickness):
context = bpy.context
scene = context.scene
layer = context.view_layer
layer_collection = context.layer_collection
if layer_collection is None:
scene_collection = scene.master_collection.collections.new("")
layer_collection = layer.collections.link(scene_collection)
else:
scene_collection = layer_collection.collection
layer_collection = context.layer_collection or layer.active_layer_collection
scene_collection = layer_collection.collection
me_tmp = bpy.data.meshes.new(name="~temp~")
bm.to_mesh(me_tmp)
@ -241,23 +237,19 @@ def object_merge(context, objects):
scene = context.scene
layer = context.view_layer
layer_collection = context.layer_collection
if layer_collection is None:
scene_collection = scene.master_collection.collections.new("")
layer_collection = layer.collections.link(scene_collection)
else:
scene_collection = layer_collection.collection
layer_collection = context.layer_collection or layer.active_layer_collection
scene_collection = layer_collection.collection
# deselect all
for obj in scene.objects:
obj.select_set('DESELECT')
obj.select_set(False)
# add empty object
mesh_base = bpy.data.meshes.new(name="~tmp~")
obj_base = bpy.data.objects.new(name="~tmp~", object_data=mesh_base)
scene_collection.objects.link(obj_base)
layer.objects.active = obj_base
obj_base.select_set('SELECT')
obj_base.select_set(True)
# loop over all meshes
for obj in objects:

View File

@ -122,7 +122,7 @@ class Print3D_ToolBar:
col = layout.column()
rowsub = col.row(align=True)
rowsub.label(text="Export Path:")
rowsub.prop(print_3d, "use_apply_scale", text="", icon='MAN_SCALE')
rowsub.prop(print_3d, "use_apply_scale", text="", icon='ORIENTATION_GLOBAL')
rowsub.prop(print_3d, "use_export_texture", text="", icon='FILE_IMAGE')
rowsub = col.row()
rowsub.prop(print_3d, "export_path", text="")

View File

@ -0,0 +1,41 @@
# ##### 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 #####
bl_info = {
"name": "Scatter Objects",
"author": "Jacques Lucke",
"version": (0, 1),
"blender": (2, 80, 0),
"location": "3D View",
"description": "Distribute object instances on another object.",
"warning": "",
"wiki_url": "",
"support": 'OFFICIAL',
"category": "Object",
}
from . import ui
from . import operator
def register():
ui.register()
operator.register()
def unregister():
ui.unregister()
operator.register()

508
object_scatter/operator.py Normal file
View File

@ -0,0 +1,508 @@
# ##### 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 #####
import bpy
import gpu
import bgl
import blf
import math
import enum
import random
from itertools import islice
from mathutils.bvhtree import BVHTree
from mathutils import Vector, Matrix, Euler
from gpu_extras.batch import batch_for_shader
from bpy_extras.view3d_utils import (
region_2d_to_vector_3d,
region_2d_to_origin_3d
)
uniform_color_shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
# Modal Operator
################################################################
class ScatterObjects(bpy.types.Operator):
bl_idname = "object.scatter"
bl_label = "Scatter Objects"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (
currently_in_3d_view(context)
and context.active_object is not None
and context.active_object.mode == 'OBJECT')
def invoke(self, context, event):
self.target_object = context.active_object
self.objects_to_scatter = get_selected_non_active_objects(context)
if self.target_object is None or len(self.objects_to_scatter) == 0:
self.report({'ERROR'}, "Select objects to scatter and a target object.")
return {'CANCELLED'}
self.base_scale = get_max_object_side_length(self.objects_to_scatter)
self.targets = []
self.active_target = None
self.target_cache = {}
self.enable_draw_callback()
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
context.area.tag_redraw()
if not event_is_in_region(event, context.region) and self.active_target is None:
return {'PASS_THROUGH'}
if event.type == 'ESC':
return self.finish('CANCELLED')
if event.type == 'RET' and event.value == 'PRESS':
self.create_scatter_object()
return self.finish('FINISHED')
event_used = self.handle_non_exit_event(event)
if event_used:
return {'RUNNING_MODAL'}
else:
return {'PASS_THROUGH'}
def handle_non_exit_event(self, event):
if self.active_target is None:
if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
self.active_target = StrokeTarget()
self.active_target.start_build(self.target_object)
return True
else:
build_state = self.active_target.continue_build(event)
if build_state == BuildState.FINISHED:
self.targets.append(self.active_target)
self.active_target = None
self.remove_target_from_cache(self.active_target)
return True
return False
def enable_draw_callback(self):
self._draw_callback_view = bpy.types.SpaceView3D.draw_handler_add(self.draw_view, (), 'WINDOW', 'POST_VIEW')
self._draw_callback_px = bpy.types.SpaceView3D.draw_handler_add(self.draw_px, (), 'WINDOW', 'POST_PIXEL')
def disable_draw_callback(self):
bpy.types.SpaceView3D.draw_handler_remove(self._draw_callback_view, 'WINDOW')
bpy.types.SpaceView3D.draw_handler_remove(self._draw_callback_px, 'WINDOW')
def draw_view(self):
for target in self.iter_targets():
target.draw()
draw_matrices_batches(list(self.iter_matrix_batches()))
def draw_px(self):
draw_text((20, 20, 0), "Instances: " + str(len(self.get_all_matrices())))
def finish(self, return_value):
self.disable_draw_callback()
bpy.context.area.tag_redraw()
return {return_value}
def create_scatter_object(self):
matrix_chunks = make_random_chunks(
self.get_all_matrices(), len(self.objects_to_scatter))
collection = bpy.data.collections.new("Scatter")
bpy.context.collection.children.link(collection)
for obj, matrices in zip(self.objects_to_scatter, matrix_chunks):
make_duplicator(collection, obj, matrices)
def get_all_matrices(self):
settings = self.get_current_settings()
matrices = []
for target in self.iter_targets():
self.ensure_target_is_in_cache(target)
matrices.extend(self.target_cache[target].get_matrices(settings))
return matrices
def iter_matrix_batches(self):
settings = self.get_current_settings()
for target in self.iter_targets():
self.ensure_target_is_in_cache(target)
yield self.target_cache[target].get_batch(settings)
def iter_targets(self):
yield from self.targets
if self.active_target is not None:
yield self.active_target
def ensure_target_is_in_cache(self, target):
if target not in self.target_cache:
entry = TargetCacheEntry(target, self.base_scale)
self.target_cache[target] = entry
def remove_target_from_cache(self, target):
self.target_cache.pop(self.active_target, None)
def get_current_settings(self):
return bpy.context.scene.scatter_properties.to_settings()
class TargetCacheEntry:
def __init__(self, target, base_scale):
self.target = target
self.last_used_settings = None
self.base_scale = base_scale
self.settings_changed()
def get_matrices(self, settings):
self._handle_new_settings(settings)
if self.matrices is None:
self.matrices = self.target.get_matrices(settings)
return self.matrices
def get_batch(self, settings):
self._handle_new_settings(settings)
if self.gpu_batch is None:
self.gpu_batch = create_batch_for_matrices(self.get_matrices(settings), self.base_scale)
return self.gpu_batch
def _handle_new_settings(self, settings):
if settings != self.last_used_settings:
self.settings_changed()
self.last_used_settings = settings
def settings_changed(self):
self.matrices = None
self.gpu_batch = None
# Duplicator Creation
######################################################
def make_duplicator(target_collection, source_object, matrices):
triangle_scale = 0.1
duplicator = triangle_object_from_matrices(source_object.name + " Duplicator", matrices, triangle_scale)
duplicator.dupli_type = 'FACES'
duplicator.use_dupli_faces_scale = True
duplicator.show_duplicator_for_viewport = True
duplicator.show_duplicator_for_render = False
duplicator.dupli_faces_scale = 1 / triangle_scale
copy_obj = source_object.copy()
copy_obj.name = source_object.name + " - copy"
copy_obj.hide_viewport = True
copy_obj.hide_render = True
copy_obj.location = (0, 0, 0)
copy_obj.parent = duplicator
target_collection.objects.link(duplicator)
target_collection.objects.link(copy_obj)
def triangle_object_from_matrices(name, matrices, triangle_scale):
mesh = triangle_mesh_from_matrices(name, matrices, triangle_scale)
return bpy.data.objects.new(name, mesh)
def triangle_mesh_from_matrices(name, matrices, triangle_scale):
mesh = bpy.data.meshes.new(name)
vertices, polygons = mesh_data_from_matrices(matrices, triangle_scale)
mesh.from_pydata(vertices, [], polygons)
mesh.update()
mesh.validate()
return mesh
unit_triangle_vertices = (
Vector((-3**-0.25, -3**-0.75, 0)),
Vector((3**-0.25, -3**-0.75, 0)),
Vector((0, 2/3**0.75, 0)))
def mesh_data_from_matrices(matrices, triangle_scale):
vertices = []
polygons = []
triangle_vertices = [triangle_scale * v for v in unit_triangle_vertices]
for i, matrix in enumerate(matrices):
vertices.extend((matrix @ v for v in triangle_vertices))
polygons.append((i * 3 + 0, i * 3 + 1, i * 3 + 2))
return vertices, polygons
# Target Provider
#################################################
class BuildState(enum.Enum):
FINISHED = enum.auto()
ONGOING = enum.auto()
class TargetProvider:
def start_build(self, target_object):
pass
def continue_build(self, event):
return BuildState.FINISHED
def get_matrices(self, scatter_settings):
return []
def draw(self):
pass
class StrokeTarget(TargetProvider):
def start_build(self, target_object):
self.points = []
self.bvhtree = bvhtree_from_object(target_object)
self.batch = None
def continue_build(self, event):
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
return BuildState.FINISHED
mouse_pos = (event.mouse_region_x, event.mouse_region_y)
location, *_ = shoot_region_2d_ray(self.bvhtree, mouse_pos)
if location is not None:
self.points.append(location)
self.batch = None
return BuildState.ONGOING
def draw(self):
if self.batch is None:
self.batch = create_line_strip_batch(self.points)
draw_line_strip_batch(self.batch, color=(1.0, 0.4, 0.1, 1.0), thickness=5)
def get_matrices(self, scatter_settings):
return scatter_around_stroke(self.points, self.bvhtree, scatter_settings)
def scatter_around_stroke(stroke_points, bvhtree, settings):
scattered_matrices = []
for point, local_seed in iter_points_on_stroke_with_seed(stroke_points, settings.density, settings.seed):
matrix = scatter_from_source_point(bvhtree, point, local_seed, settings)
scattered_matrices.append(matrix)
return scattered_matrices
def iter_points_on_stroke_with_seed(stroke_points, density, seed):
for i, (start, end) in enumerate(iter_pairwise(stroke_points)):
segment_seed = sub_seed(seed, i)
segment_vector = end - start
segment_length = segment_vector.length
amount = round_random(segment_length * density, segment_seed)
for j in range(amount):
t = random_uniform(sub_seed(segment_seed, j, 0))
origin = start + t * segment_vector
yield origin, sub_seed(segment_seed, j, 1)
def scatter_from_source_point(bvhtree, point, seed, settings):
# Project displaced point on surface
radius = random_uniform(sub_seed(seed, 0)) * settings.radius
offset = random_vector(sub_seed(seed, 2)) * radius
location, normal, *_ = bvhtree.find_nearest(point + offset)
assert location is not None
normal.normalize()
# Scale
min_scale = settings.scale * (1 - settings.random_scale)
max_scale = settings.scale
scale = random_uniform(sub_seed(seed, 1), min_scale, max_scale)
# Location
location += normal * settings.normal_offset * scale
# Rotation
z_rotation = Euler((0, 0, random_uniform(sub_seed(seed, 3), 0, 2 * math.pi))).to_matrix()
normal_rotation = normal.to_track_quat('Z', 'X').to_matrix()
local_rotation = random_euler(sub_seed(seed, 3), settings.rotation).to_matrix()
rotation = local_rotation @ normal_rotation @ z_rotation
return Matrix.Translation(location) @ rotation.to_4x4() @ scale_matrix(scale)
# Drawing
#################################################
box_vertices = (
(-1, -1, 1), ( 1, -1, 1), ( 1, 1, 1), (-1, 1, 1),
(-1, -1, -1), ( 1, -1, -1), ( 1, 1, -1), (-1, 1, -1))
box_indices = (
(0, 1, 2), (2, 3, 0), (1, 5, 6), (6, 2, 1),
(7, 6, 5), (5, 4, 7), (4, 0, 3), (3, 7, 4),
(4, 5, 1), (1, 0, 4), (3, 2, 6), (6, 7, 3))
box_vertices = tuple(Vector(vertex) * 0.5 for vertex in box_vertices)
def draw_matrices_batches(batches):
uniform_color_shader.bind()
uniform_color_shader.uniform_float("color", (0.4, 0.4, 1.0, 0.3))
bgl.glEnable(bgl.GL_BLEND)
bgl.glDepthMask(bgl.GL_FALSE)
for batch in batches:
batch.draw(uniform_color_shader)
bgl.glDisable(bgl.GL_BLEND)
bgl.glDepthMask(bgl.GL_TRUE)
def create_batch_for_matrices(matrices, base_scale):
coords = []
indices = []
scaled_box_vertices = [base_scale * vertex for vertex in box_vertices]
for matrix in matrices:
offset = len(coords)
coords.extend((matrix @ vertex for vertex in scaled_box_vertices))
indices.extend(tuple(index + offset for index in element) for element in box_indices)
batch = batch_for_shader(uniform_color_shader, 'TRIS', {"pos" : coords}, indices = indices)
return batch
def draw_line_strip_batch(batch, color, thickness=1):
bgl.glLineWidth(thickness)
uniform_color_shader.bind()
uniform_color_shader.uniform_float("color", color)
batch.draw(uniform_color_shader)
def create_line_strip_batch(coords):
return batch_for_shader(uniform_color_shader, 'LINE_STRIP', {"pos" : coords})
def draw_text(location, text, size=15, color=(1, 1, 1, 1)):
font_id = 0
blf.position(font_id, *location)
blf.size(font_id, size, 72)
blf.draw(font_id, text)
# Utilities
########################################################
'''
Pythons random functions are designed to be used in cases
when a seed is set once and then many random numbers are
generated. To improve the user experience I want to have
full control over how random seeds propagate through the
functions. This is why I use custom random functions.
One benefit is that changing the object density does not
generate new random positions for all objects.
'''
def round_random(value, seed):
probability = value % 1
if probability < random_uniform(seed):
return math.floor(value)
else:
return math.ceil(value)
def random_vector(x, min=-1, max=1):
return Vector((
random_uniform(sub_seed(x, 0), min, max),
random_uniform(sub_seed(x, 1), min, max),
random_uniform(sub_seed(x, 2), min, max)))
def random_euler(x, factor):
return Euler(tuple(random_vector(x) * factor))
def random_uniform(x, min=0, max=1):
return random_int(x) / 2147483648 * (max - min) + min
def random_int(x):
x = (x<<13) ^ x
return (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff
def sub_seed(seed, index, index2=0):
return random_int(seed * 3243 + index * 5643 + index2 * 54243)
def currently_in_3d_view(context):
return context.space_data.type == 'VIEW_3D'
def get_selected_non_active_objects(context):
return set(context.selected_objects) - {context.active_object}
def make_random_chunks(sequence, chunk_amount):
sequence = list(sequence)
random.shuffle(sequence)
return make_chunks(sequence, chunk_amount)
def make_chunks(sequence, chunk_amount):
length = math.ceil(len(sequence) / chunk_amount)
return [sequence[i:i+length] for i in range(0, len(sequence), length)]
def iter_pairwise(sequence):
return zip(sequence, islice(sequence, 1, None))
def bvhtree_from_object(object):
import bmesh
bm = bmesh.new()
mesh = object.to_mesh(bpy.context.depsgraph, True)
bm.from_mesh(mesh)
bm.transform(object.matrix_world)
bvhtree = BVHTree.FromBMesh(bm)
bpy.data.meshes.remove(mesh)
return bvhtree
def shoot_region_2d_ray(bvhtree, position_2d):
region = bpy.context.region
region_3d = bpy.context.space_data.region_3d
origin = region_2d_to_origin_3d(region, region_3d, position_2d)
direction = region_2d_to_vector_3d(region, region_3d, position_2d)
location, normal, index, distance = bvhtree.ray_cast(origin, direction)
return location, normal, index, distance
def scale_matrix(factor):
m = Matrix.Identity(4)
m[0][0] = factor
m[1][1] = factor
m[2][2] = factor
return m
def event_is_in_region(event, region):
return (region.x <= event.mouse_x <= region.x + region.width
and region.y <= event.mouse_y <= region.y + region.height)
def get_max_object_side_length(objects):
return max(
max(obj.dimensions[0] for obj in objects),
max(obj.dimensions[1] for obj in objects),
max(obj.dimensions[2] for obj in objects)
)
# Registration
###############################################
def register():
bpy.utils.register_class(ScatterObjects)
def unregister():
bpy.utils.unregister_class(ScatterObjects)

138
object_scatter/ui.py Normal file
View File

@ -0,0 +1,138 @@
# ##### 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 #####
import bpy
import math
from collections import namedtuple
from bpy.props import (
IntProperty,
FloatProperty,
PointerProperty
)
ScatterSettings = namedtuple("ScatterSettings",
["seed", "density", "radius", "scale", "random_scale",
"rotation", "normal_offset"])
class ObjectScatterProperties(bpy.types.PropertyGroup):
seed: IntProperty(
name="Seed",
default=0
)
density: FloatProperty(
name="Density",
default=10,
min=0,
soft_max=50
)
radius: FloatProperty(
name="Radius",
default=1,
min=0,
soft_max=5,
subtype='DISTANCE',
unit='LENGTH'
)
scale: FloatProperty(
name="Scale",
default=0.3,
min=0.00001,
soft_max=1
)
random_scale_percentage: FloatProperty(
name="Random Scale Percentage",
default=80,
min=0,
max=100,
subtype='PERCENTAGE',
precision=0
)
rotation: FloatProperty(
name="Rotation",
default=0.5,
min=0,
max=math.pi * 2,
soft_max=math.pi / 2,
subtype='ANGLE',
unit='ROTATION'
)
normal_offset: FloatProperty(
name="Normal Offset",
default=0,
soft_min=-1.0,
soft_max=1.0
)
def to_settings(self):
return ScatterSettings(
seed=self.seed,
density=self.density,
radius=self.radius,
scale=self.scale,
random_scale=self.random_scale_percentage / 100,
rotation=self.rotation,
normal_offset=self.normal_offset,
)
class ObjectScatterPanel(bpy.types.Panel):
bl_label = "Object Scatter"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = '.objectmode'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scatter = context.scene.scatter_properties
layout.prop(scatter, "density", slider=True)
layout.prop(scatter, "radius", slider=True)
col = layout.column(align=True)
col.prop(scatter, "scale", slider=True)
col.prop(scatter, "random_scale_percentage", text="Randomness", slider=True)
layout.prop(scatter, "rotation", slider=True)
layout.prop(scatter, "normal_offset", text="Offset", slider=True)
layout.prop(scatter, "seed")
classes = (
ObjectScatterProperties,
ObjectScatterPanel,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.scatter_properties = PointerProperty(type=ObjectScatterProperties)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
del bpy.types.Scene.scatter_properties

View File

@ -449,7 +449,7 @@ class ModalIndexOperator(Operator):
self.tsize -= 1
elif event.type in {'RIGHTMOUSE', 'ESC', 'TAB'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
context.area.header_text_set("")
context.area.header_text_set(None)
return {'CANCELLED'}
return {'PASS_THROUGH'}

View File

@ -116,8 +116,8 @@ def generate_rig(context, metarig):
obj.animation_data_clear()
# Select generated rig object
metarig.select_set('DESELECT')
obj.select_set('SELECT')
metarig.select_set(False)
obj.select_set(True)
view_layer.objects.active = obj
# Remove wgts if force update is set
@ -125,7 +125,7 @@ def generate_rig(context, metarig):
if wgts_group_name in scene.objects and id_store.rigify_force_widget_update:
bpy.ops.object.select_all(action='DESELECT')
for wgt in bpy.data.objects[wgts_group_name].children:
wgt.select_set('SELECT')
wgt.select_set(True)
bpy.ops.object.delete(use_global=False)
if rig_old_name:
bpy.data.objects[wgts_group_name].name = "WGTS_" + obj.name
@ -154,9 +154,9 @@ def generate_rig(context, metarig):
# Select the temp rigs for merging
for objt in scene.objects:
objt.select_set('DESELECT') # deselect all objects
temp_rig_1.select_set('SELECT')
temp_rig_2.select_set('SELECT')
objt.select_set(False) # deselect all objects
temp_rig_1.select_set(True)
temp_rig_2.select_set(True)
view_layer.objects.active = temp_rig_2
# Merge the temporary rigs
@ -167,8 +167,8 @@ def generate_rig(context, metarig):
# Select the generated rig
for objt in scene.objects:
objt.select_set('DESELECT') # deselect all objects
obj.select_set('SELECT')
objt.select_set(False) # deselect all objects
obj.select_set(True)
view_layer.objects.active = obj
# Copy over bone properties
@ -318,7 +318,7 @@ def generate_rig(context, metarig):
# if id_store.rigify_generate_mode == 'new':
# bpy.ops.object.select_all(action='DESELECT')
# for wgt in bpy.data.objects[wgts_group_name].children:
# wgt.select_set('SELECT')
# wgt.select_set(True)
# bpy.ops.object.make_single_user(obdata=True)
#----------------------------------
@ -336,7 +336,7 @@ def generate_rig(context, metarig):
# Go into editmode in the rig armature
bpy.ops.object.mode_set(mode='OBJECT')
context.view_layer.objects.active = obj
obj.select_set('SELECT')
obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
scripts = rig.generate()
if scripts is not None:
@ -521,7 +521,7 @@ def generate_rig(context, metarig):
#----------------------------------
# Restore active collection
view_layer.collections.active = layer_collection
view_layer.active_layer_collection = layer_collection
def create_selection_sets(obj, metarig):
@ -534,8 +534,8 @@ def create_selection_sets(obj, metarig):
bpy.ops.object.mode_set(mode='POSE')
bpy.context.view_layer.objects.active = obj
obj.select_set('SELECT')
metarig.select_set('DESELECT')
obj.select_set(True)
metarig.select_set(False)
pbones = obj.pose.bones
for i, name in enumerate(metarig.data.rigify_layers.keys()):

View File

@ -101,8 +101,8 @@ def generate_rig(context, metarig):
obj.animation_data_clear()
# Select generated rig object
metarig.select_set('DESELECT')
obj.select_set('SELECT')
metarig.select_set(False)
obj.select_set(True)
view_layer.objects.active = obj
# Remove all bones from the generated rig armature.
@ -122,9 +122,9 @@ def generate_rig(context, metarig):
# Select the temp rigs for merging
for objt in scene.objects:
objt.select_set('DESELECT') # deselect all objects
temp_rig_1.select_set('SELECT')
temp_rig_2.select_set('SELECT')
objt.select_set(False) # deselect all objects
temp_rig_1.select_set(True)
temp_rig_2.select_set(True)
view_layer.objects.active = temp_rig_2
# Merge the temporary rigs
@ -135,8 +135,8 @@ def generate_rig(context, metarig):
# Select the generated rig
for objt in scene.objects:
objt.select_set('DESELECT') # deselect all objects
obj.select_set('SELECT')
objt.select_set(False) # deselect all objects
obj.select_set(True)
view_layer.objects.active = obj
# Copy over bone properties
@ -281,7 +281,7 @@ def generate_rig(context, metarig):
# Go into editmode in the rig armature
bpy.ops.object.mode_set(mode='OBJECT')
context.view_layer.objects.active = obj
obj.select_set('SELECT')
obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
scripts = rig.generate()
if scripts is not None:
@ -435,7 +435,7 @@ def generate_rig(context, metarig):
#----------------------------------
# Restore active collection
view_layer.collections.active = layer_collection
view_layer.active_layer_collection = layer_collection
def get_bone_rigs(obj, bone_name, halt_on_missing=False):

View File

@ -965,9 +965,11 @@ def ensure_widget_collection(context):
collection.children.link(widget_collection)
widget_layer_collection = [c for c in layer_collection.children if c.collection == widget_collection][0]
elif widget_collection == view_layer.layer_collection.collection:
widget_layer_collection = view_layer.layer_collection
else:
widget_layer_collection = get_layer_collection_from_collection(view_layer.collections, widget_collection)
widget_layer_collection = get_layer_collection_from_collection(view_layer.layer_collection.children, widget_collection)
# Make the widget the active collection for the upcoming added (widget) objects
view_layer.collections.active = widget_layer_collection
view_layer.active_layer_collection = widget_layer_collection
return widget_collection

View File

@ -1290,9 +1290,11 @@ def ensure_widget_collection(context):
collection.children.link(widget_collection)
widget_layer_collection = [c for c in layer_collection.children if c.collection == widget_collection][0]
elif widget_collection == view_layer.layer_collection.collection:
widget_layer_collection = view_layer.layer_collection
else:
widget_layer_collection = get_layer_collection_from_collection(view_layer.collections, widget_collection)
widget_layer_collection = get_layer_collection_from_collection(view_layer.layer_collection.children, widget_collection)
# Make the widget the active collection for the upcoming added (widget) objects
view_layer.collections.active = widget_layer_collection
view_layer.active_layer_collection = widget_layer_collection
return widget_collection

View File

@ -321,7 +321,7 @@ class JumptoCut(Panel):
if prefs.kr_mini_ui:
row = layout.row(align=True)
row.operator("sequencerextra.extrasnap", text="", icon="SNAP_ON").align = 0
row.operator("sequencerextra.extrasnap", text="", icon="SNAP_SURFACE").align = 1
row.operator("sequencerextra.extrasnap", text="", icon="NONE").align = 1
row.operator("sequencerextra.extrasnap", text="", icon="SNAP_ON").align = 2
row.separator()

View File

@ -254,7 +254,7 @@ class VIEW3D_PT_3dnavigationPanel(Panel):
col = layout.column(align=True)
col.label(text="Cursor:", icon='PIVOT_CURSOR')
row = col.row(align=True)
row.operator("view3d.snap_cursor_to_center", text="Center")
row.operator("view3d.snap_cursor_to_center", text="World Origin")
row.operator("view3d.view_center_cursor", text="View")
col.operator("view3d.snap_cursor_to_selected", text="Cursor to Selected")

View File

@ -22,7 +22,7 @@ bl_info = {
"name": "Math Vis (Console)",
"author": "Campbell Barton",
"version": (0, 2, 1),
"blender": (2, 57, 0),
"blender": (2, 80, 0),
"location": "Properties: Scene > Math Vis Console and Python Console: Menu",
"description": "Display console defined mathutils variables in the 3D view",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
@ -74,7 +74,7 @@ class PanelConsoleVars(Panel):
if len(state_props) == 0:
box = layout.box()
col = box.column(align=True)
col.label("No vars to display")
col.label(text="No vars to display")
else:
layout.template_list(
'MathVisVarList',
@ -98,7 +98,7 @@ class DeleteVar(Operator):
bl_description = "Remove the variable from the Console"
bl_options = {'REGISTER'}
key = StringProperty(name="Key")
key: StringProperty(name="Key")
def execute(self, context):
locals = utils.console_namespace()
@ -114,7 +114,7 @@ class ToggleDisplay(Operator):
bl_description = "Change the display state of the var"
bl_options = {'REGISTER'}
key = StringProperty(name="Key")
key: StringProperty(name="Key")
def execute(self, context):
utils.VarStates.toggle_display_state(self.key)
@ -128,7 +128,7 @@ class ToggleLock(Operator):
bl_description = "Lock the var from being deleted"
bl_options = {'REGISTER'}
key = StringProperty(name="Key")
key: StringProperty(name="Key")
def execute(self, context):
utils.VarStates.toggle_lock_state(self.key)
@ -177,8 +177,8 @@ def call_console_hook(self, context):
class MathVisStateProp(PropertyGroup):
ktype = StringProperty()
state = BoolVectorProperty(default=(False, False), size=2)
ktype: StringProperty()
state: BoolVectorProperty(default=(False, False), size=2)
class MathVisVarList(UIList):
@ -219,22 +219,22 @@ class MathVisVarList(UIList):
class MathVis(PropertyGroup):
index = IntProperty(
index: IntProperty(
name="index"
)
bbox_hide = BoolProperty(
bbox_hide: BoolProperty(
name="Hide BBoxes",
default=False,
description="Hide the bounding boxes rendered for Matrix like items",
update=call_console_hook
)
name_hide = BoolProperty(
name_hide: BoolProperty(
name="Hide Names",
default=False,
description="Hide the names of the rendered items",
update=call_console_hook
)
bbox_scale = FloatProperty(
bbox_scale: FloatProperty(
name="Scale factor",
min=0, default=1,
description="Resize the Bounding Box and the coordinate "

View File

@ -20,6 +20,8 @@
import bpy
import blf
import gpu
from gpu_extras.batch import batch_for_shader
from . import utils
from mathutils import Vector
@ -27,6 +29,8 @@ from mathutils import Vector
SpaceView3D = bpy.types.SpaceView3D
callback_handle = []
single_color_shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
smooth_color_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
def tag_redraw_areas():
context = bpy.context
@ -64,28 +68,16 @@ def callback_disable():
def draw_callback_px():
context = bpy.context
from bgl import glColor3f
font_id = 0 # XXX, need to find out how best to get this.
if context.window_manager.MathVisProp.name_hide:
return
font_id = 0
blf.size(font_id, 12, 72)
data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
name_hide = context.window_manager.MathVisProp.name_hide
if name_hide:
return
if not data_matrix and not data_quat and not data_euler and not data_vector and not data_vector_array:
'''
# draw some text
glColor3f(1.0, 0.0, 0.0)
blf.position(font_id, 180, 10, 0)
blf.draw(font_id, "Python Console has no mathutils definitions")
'''
return
glColor3f(1.0, 1.0, 1.0)
region = context.region
region3d = context.space_data.region_3d
@ -93,11 +85,10 @@ def draw_callback_px():
region_mid_width = region.width / 2.0
region_mid_height = region.height / 2.0
# vars for projection
perspective_matrix = region3d.perspective_matrix.copy()
def draw_text(text, vec, dx=3.0, dy=-4.0):
vec_4d = perspective_matrix * vec.to_4d()
vec_4d = perspective_matrix @ vec.to_4d()
if vec_4d.w > 0.0:
x = region_mid_width + region_mid_width * (vec_4d.x / vec_4d.w)
y = region_mid_height + region_mid_height * (vec_4d.y / vec_4d.w)
@ -105,191 +96,138 @@ def draw_callback_px():
blf.position(font_id, x + dx, y + dy, 0.0)
blf.draw(font_id, text)
# points
if data_vector:
for key, vec in data_vector.items():
draw_text(key, vec)
# lines
if data_vector_array:
for key, vec in data_vector_array.items():
if vec:
draw_text(key, vec[0])
# matrix
if data_matrix:
for key, mat in data_matrix.items():
loc = Vector((mat[0][3], mat[1][3], mat[2][3]))
draw_text(key, loc, dx=10, dy=-20)
line = 20
offset_y = 20
if data_quat:
loc = context.scene.cursor_location.copy()
for key, mat in data_quat.items():
draw_text(key, loc, dy=-line)
line += 20
draw_text(key, loc, dy=-offset_y)
offset_y += 20
if data_euler:
loc = context.scene.cursor_location.copy()
for key, mat in data_euler.items():
draw_text(key, loc, dy=-line)
line += 20
draw_text(key, loc, dy=-offset_y)
offset_y += 20
def draw_callback_view():
context = bpy.context
from bgl import (
glEnable,
glDisable,
glColor3f,
glVertex3f,
glPointSize,
glLineWidth,
glBegin,
glEnd,
glLineStipple,
GL_POINTS,
GL_LINE_STRIP,
GL_LINES,
GL_LINE_STIPPLE
)
settings = bpy.context.window_manager.MathVisProp
scale = settings.bbox_scale
with_bounding_box = not settings.bbox_hide
data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
# draw_matrix modifiers
bbox_hide = context.window_manager.MathVisProp.bbox_hide
bbox_scale = context.window_manager.MathVisProp.bbox_scale
# draw_matrix vars
zero = Vector((0.0, 0.0, 0.0))
x_p = Vector((bbox_scale, 0.0, 0.0))
x_n = Vector((-bbox_scale, 0.0, 0.0))
y_p = Vector((0.0, bbox_scale, 0.0))
y_n = Vector((0.0, -bbox_scale, 0.0))
z_p = Vector((0.0, 0.0, bbox_scale))
z_n = Vector((0.0, 0.0, -bbox_scale))
bb = [Vector() for i in range(8)]
def draw_matrix(mat):
zero_tx = mat * zero
glLineWidth(2.0)
# x
glColor3f(1.0, 0.2, 0.2)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * x_p))
glEnd()
glColor3f(0.6, 0.0, 0.0)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * x_n))
glEnd()
# y
glColor3f(0.2, 1.0, 0.2)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * y_p))
glEnd()
glColor3f(0.0, 0.6, 0.0)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * y_n))
glEnd()
# z
glColor3f(0.4, 0.4, 1.0)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * z_p))
glEnd()
glColor3f(0.0, 0.0, 0.6)
glBegin(GL_LINES)
glVertex3f(*(zero_tx))
glVertex3f(*(mat * z_n))
glEnd()
# bounding box
if bbox_hide:
return
i = 0
glColor3f(1.0, 1.0, 1.0)
for x in (-bbox_scale, bbox_scale):
for y in (-bbox_scale, bbox_scale):
for z in (-bbox_scale, bbox_scale):
bb[i][:] = x, y, z
bb[i] = mat * bb[i]
i += 1
# strip
glLineWidth(1.0)
glLineStipple(1, 0xAAAA)
glEnable(GL_LINE_STIPPLE)
glBegin(GL_LINE_STRIP)
for i in 0, 1, 3, 2, 0, 4, 5, 7, 6, 4:
glVertex3f(*bb[i])
glEnd()
# not done by the strip
glBegin(GL_LINES)
glVertex3f(*bb[1])
glVertex3f(*bb[5])
glVertex3f(*bb[2])
glVertex3f(*bb[6])
glVertex3f(*bb[3])
glVertex3f(*bb[7])
glEnd()
glDisable(GL_LINE_STIPPLE)
# points
if data_vector:
glPointSize(3.0)
glBegin(GL_POINTS)
glColor3f(0.5, 0.5, 1)
for key, vec in data_vector.items():
glVertex3f(*vec.to_3d())
glEnd()
glPointSize(1.0)
coords = [tuple(vec.to_3d()) for vec in data_vector.values()]
draw_points(coords)
# lines
if data_vector_array:
glColor3f(0.5, 0.5, 1)
glLineWidth(2.0)
for line in data_vector_array.values():
glBegin(GL_LINE_STRIP)
for vec in line:
glVertex3f(*vec)
glEnd()
glPointSize(1.0)
coords = [tuple(vec.to_3d()) for vec in line]
draw_line(coords)
glLineWidth(1.0)
# matrix
if data_matrix:
for mat in data_matrix.values():
draw_matrix(mat)
draw_matrices(list(data_matrix.values()), scale, with_bounding_box)
if data_quat:
loc = context.scene.cursor_location.copy()
if data_euler or data_quat:
cursor = bpy.context.scene.cursor_location.copy()
derived_matrices = []
for quat in data_quat.values():
mat = quat.to_matrix().to_4x4()
mat.translation = loc
draw_matrix(mat)
if data_euler:
loc = context.scene.cursor_location.copy()
matrix = quat.to_matrix().to_4x4()
matrix.translation = cursor
derived_matrices.append(matrix)
for eul in data_euler.values():
mat = eul.to_matrix().to_4x4()
mat.translation = loc
draw_matrix(mat)
matrix = eul.to_matrix().to_4x4()
matrix.translation = cursor
derived_matrices.append(matrix)
draw_matrices(derived_matrices, scale, with_bounding_box)
def draw_points(points):
batch = batch_from_points(points, "POINTS")
single_color_shader.bind()
single_color_shader.uniform_float("color", (0.5, 0.5, 1, 1))
batch.draw(single_color_shader)
def draw_line(points):
batch = batch_from_points(points, "LINE_STRIP")
single_color_shader.bind()
single_color_shader.uniform_float("color", (0.5, 0.5, 1, 1))
batch.draw(single_color_shader)
def batch_from_points(points, type):
return batch_for_shader(single_color_shader, type, {"pos" : points})
def draw_matrices(matrices, scale, with_bounding_box):
x_p = Vector(( scale, 0.0, 0.0))
x_n = Vector((-scale, 0.0, 0.0))
y_p = Vector((0.0, scale, 0.0))
y_n = Vector((0.0, -scale, 0.0))
z_p = Vector((0.0, 0.0, scale))
z_n = Vector((0.0, 0.0, -scale))
red_dark = (0.2, 0.0, 0.0, 1.0)
red_light = (1.0, 0.2, 0.2, 1.0)
green_dark = (0.0, 0.2, 0.0, 1.0)
green_light = (0.2, 1.0, 0.2, 1.0)
blue_dark = (0.0, 0.0, 0.2, 1.0)
blue_light = (0.4, 0.4, 1.0, 1.0)
coords = []
colors = []
for matrix in matrices:
coords.append(matrix @ x_n)
coords.append(matrix @ x_p)
colors.extend((red_dark, red_light))
coords.append(matrix @ y_n)
coords.append(matrix @ y_p)
colors.extend((green_dark, green_light))
coords.append(matrix @ z_n)
coords.append(matrix @ z_p)
colors.extend((blue_dark, blue_light))
batch = batch_for_shader(smooth_color_shader, "LINES", {
"pos" : coords,
"color" : colors
})
batch.draw(smooth_color_shader)
if with_bounding_box:
draw_bounding_boxes(matrices, scale, (1.0, 1.0, 1.0, 1.0))
def draw_bounding_boxes(matrices, scale, color):
boundbox_points = []
for x in (-scale, scale):
for y in (-scale, scale):
for z in (-scale, scale):
boundbox_points.append(Vector((x, y, z)))
boundbox_lines = [
(0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5),
(5, 7), (7, 6), (6, 4), (1, 5), (2, 6), (3, 7)
]
points = []
for matrix in matrices:
for v1, v2 in boundbox_lines:
points.append(matrix @ boundbox_points[v1])
points.append(matrix @ boundbox_points[v2])
batch = batch_from_points(points, "LINES")
single_color_shader.bind()
single_color_shader.uniform_float("color", color)
batch.draw(single_color_shader)

View File

@ -162,6 +162,28 @@ class PieToolsPreferences(AddonPreferences):
update=disable_all_modules
)
for mod in sub_modules:
mod_name = mod.__name__.split('.')[-1]
def gen_update(mod, use_prop_name):
def update(self, context):
if getattr(self, use_prop_name):
if not mod.__addon_enabled__:
register_submodule(mod)
else:
if mod.__addon_enabled__:
unregister_submodule(mod)
return update
use_prop_name = 'use_' + mod_name
__annotations__[use_prop_name] = BoolProperty(
name=mod.bl_info['name'],
description=mod.bl_info.get('description', ''),
update=gen_update(mod, use_prop_name),
)
__annotations__['show_expanded_' + mod_name] = BoolProperty()
def draw(self, context):
layout = self.layout
split = layout.split(factor=0.5, align=True)
@ -259,29 +281,6 @@ class PieToolsPreferences(AddonPreferences):
icon="FILE_PARENT")
for mod in sub_modules:
info = mod.bl_info
mod_name = mod.__name__.split('.')[-1]
def gen_update(mod):
def update(self, context):
if getattr(self, 'use_' + mod.__name__.split('.')[-1]):
if not mod.__addon_enabled__:
register_submodule(mod)
else:
if mod.__addon_enabled__:
unregister_submodule(mod)
return update
prop = BoolProperty(
name=info['name'],
description=info.get('description', ''),
update=gen_update(mod),
)
setattr(PieToolsPreferences, 'use_' + mod_name, prop)
prop = BoolProperty()
setattr(PieToolsPreferences, 'show_expanded_' + mod_name, prop)
classes = (
PieToolsPreferences,
)

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "V/E/F Align tools",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 2),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "Mesh Edit Mode",
"warning": "",
"wiki_url": "",
@ -68,7 +68,7 @@ class PieAlign(Menu):
box = pie.split().box().column()
row = box.row(align=True)
row.label("X")
row.label(text="X")
align_1 = row.operator("alignxyz.all", text="Neg")
align_1.axis = '0'
align_1.side = 'NEGATIVE'
@ -77,7 +77,7 @@ class PieAlign(Menu):
align_2.side = 'POSITIVE'
row = box.row(align=True)
row.label("Y")
row.label(text="Y")
align_3 = row.operator("alignxyz.all", text="Neg")
align_3.axis = '1'
align_3.side = 'NEGATIVE'
@ -86,7 +86,7 @@ class PieAlign(Menu):
align_4.side = 'POSITIVE'
row = box.row(align=True)
row.label("Z")
row.label(text="Z")
align_5 = row.operator("alignxyz.all", text="Neg")
align_5.axis = '2'
align_5.side = 'NEGATIVE'

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Pie menu for Timeline controls",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Apply Transform Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -49,15 +49,15 @@ class PieApplyTransforms(Menu):
# 4 - LEFT
pie.operator("apply.transformall", text="Apply All", icon='FREEZE')
# 6 - RIGHT
pie.operator("clear.all", text="Clear All", icon='MANIPUL')
pie.operator("clear.all", text="Clear All", icon='NONE')
# 2 - BOTTOM
pie.operator("object.duplicates_make_real", text="Make Duplicates Real")
# 8 - TOP
pie.operator("apply.transformlocrotscale", text="Rotation", icon='MAN_ROT').option = 'ROT'
pie.operator("apply.transformlocrotscale", text="Rotation", icon='NONE').option = 'ROT'
# 7 - TOP - LEFT
pie.operator("apply.transformlocrotscale", text="Location", icon='MAN_ROT').option = 'LOC'
pie.operator("apply.transformlocrotscale", text="Location", icon='NONE').option = 'LOC'
# 9 - TOP - RIGHT
pie.operator("apply.transformlocrotscale", text="Scale", icon='MAN_ROT').option = 'SCALE'
pie.operator("apply.transformlocrotscale", text="Scale", icon='NONE').option = 'SCALE'
# 1 - BOTTOM - LEFT
pie.operator("object.visual_transform_apply", text="Visual Transforms")
# 3 - BOTTOM - RIGHT
@ -109,10 +109,10 @@ class ClearMenu(Menu):
def draw(self, context):
layout = self.layout
layout.operator("object.location_clear", text="Clear Location", icon='MAN_TRANS')
layout.operator("object.rotation_clear", text="Clear Rotation", icon='MAN_ROT')
layout.operator("object.scale_clear", text="Clear Scale", icon='MAN_SCALE')
layout.operator("object.origin_clear", text="Clear Origin", icon='MANIPUL')
layout.operator("object.location_clear", text="Clear Location", icon='NONE')
layout.operator("object.rotation_clear", text="Clear Rotation", icon='NONE')
layout.operator("object.scale_clear", text="Clear Scale", icon='NONE')
layout.operator("object.origin_clear", text="Clear Origin", icon='NONE')
# Clear all

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Cursor Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 0),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -69,16 +69,16 @@ class Snap_CursorMenu(Menu):
icon='CLIPUV_HLT').use_offset = False
# 6 - RIGHT
pie.operator("view3d.snap_selected_to_cursor",
text="Selection to Cursor (Keep Offset)", icon='CURSOR').use_offset = True
text="Selection to Cursor (Keep Offset)", icon='PIVOT_CURSOR').use_offset = True
# 2 - BOTTOM
pie.operator("view3d.snap_cursor_selected_to_center1",
text="Selected & Cursor to Center", icon='ALIGN')
text="Selected & Cursor to Center", icon='NONE')
# 8 - TOP
pie.operator("view3d.snap_cursor_to_center", text="Cursor to Center", icon='CLIPUV_DEHLT')
pie.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin", icon='CLIPUV_DEHLT')
# 7 - TOP - LEFT
pie.operator("view3d.snap_cursor_to_selected", text="Cursor to Selected", icon='ROTACTIVE')
pie.operator("view3d.snap_cursor_to_selected", text="Cursor to Selected", icon='NONE')
# 9 - TOP - RIGHT
pie.operator("view3d.snap_cursor_to_active", text="Cursor to Active", icon='BBOX')
pie.operator("view3d.snap_cursor_to_active", text="Cursor to Active", icon='NONE')
# 1 - BOTTOM - LEFT
pie.operator("view3d.snap_selected_to_grid", text="Selection to Grid", icon='GRID')
# 3 - BOTTOM - RIGHT

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Edit mode V/E/F Delete Modes",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 0),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "Mesh Edit Mode",
"warning": "",
"wiki_url": "",
@ -57,13 +57,13 @@ class PieDelete(Menu):
# 1 - BOTTOM - LEFT
box = pie.split().column()
box.operator("mesh.dissolve_limited", text="Limited Dissolve", icon='STICKY_UVS_LOC')
box.operator("mesh.delete_edgeloop", text="Delete Edge Loops", icon='BORDER_LASSO')
box.operator("mesh.delete_edgeloop", text="Delete Edge Loops", icon='NONE')
box.operator("mesh.edge_collapse", text="Edge Collapse", icon='UV_EDGESEL')
# 3 - BOTTOM - RIGHT
box = pie.split().column()
box.operator("mesh.delete", text="Only Edge & Faces", icon='SPACE2').type = 'EDGE_FACE'
box.operator("mesh.delete", text="Only Edge & Faces", icon='NONE').type = 'EDGE_FACE'
box.operator("mesh.delete", text="Only Faces", icon='UV_FACESEL').type = 'ONLY_FACE'
box.operator("mesh.remove_doubles", text="Remove Doubles", icon='ORTHO')
box.operator("mesh.remove_doubles", text="Remove Doubles", icon='NONE')
classes = (

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Switch Editor Type Menu",
"author": "saidenka",
"version": (0, 1, 0),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "All Editors",
"warning": "",
"wiki_url": "",
@ -73,15 +73,15 @@ class AreaPieEditor(Menu):
# 6 - RIGHT
pie.menu(AreaTypePieAnim.bl_idname, text="Animation Editors", icon="ACTION")
# 2 - BOTTOM
pie.operator(SetAreaType.bl_idname, text="Property", icon="BUTS").types = "PROPERTIES"
pie.operator(SetAreaType.bl_idname, text="Property", icon="PROPERTIES").types = "PROPERTIES"
# 8 - TOP
pie.operator(SetAreaType.bl_idname, text="3D View", icon="MESH_CUBE").types = "VIEW_3D"
# 7 - TOP - LEFT
pie.operator(SetAreaType.bl_idname, text="UV/Image Editor", icon="IMAGE_COL").types = "IMAGE_EDITOR"
pie.operator(SetAreaType.bl_idname, text="UV/Image Editor", icon="NONE").types = "IMAGE_EDITOR"
# 9 - TOP - RIGHT
pie.operator(SetAreaType.bl_idname, text="Node Editor", icon="NODETREE").types = "NODE_EDITOR"
# 1 - BOTTOM - LEFT
pie.operator(SetAreaType.bl_idname, text="Outliner", icon="OOPS").types = "OUTLINER"
pie.operator(SetAreaType.bl_idname, text="Outliner", icon="NONE").types = "OUTLINER"
# 3 - BOTTOM - RIGHT
pie.menu(AreaTypePieOther.bl_idname, text="More Editors", icon="QUESTION")

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Extended Manipulator Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -44,13 +44,7 @@ class WManupulators(Operator):
bl_description = " Show/Hide Manipulator"
def execute(self, context):
if context.space_data.show_manipulator is True:
context.space_data.show_manipulator = False
elif context.space_data.show_manipulator is False:
context.space_data.show_manipulator = True
context.space_data.show_gizmo_tool = not context.space_data.show_gizmo_tool
return {'FINISHED'}
@ -63,13 +57,13 @@ class PieManipulator(Menu):
layout = self.layout
pie = layout.menu_pie()
# 4 - LEFT
pie.operator("wm.tool_set_by_name", text="Translate", icon='MAN_TRANS').name = "Move"
pie.operator("wm.tool_set_by_name", text="Translate", icon='NONE').name = "Move"
# 6 - RIGHT
pie.operator("wm.tool_set_by_name", text="Scale", icon='MAN_SCALE').name = "Scale"
pie.operator("wm.tool_set_by_name", text="Rotate", icon='NONE').name = "Rotate"
# 2 - BOTTOM
pie.operator("wm.tool_set_by_name", text="Rotate", icon='MAN_ROT').name = "Rotate"
pie.operator("wm.tool_set_by_name", text="Scale", icon='NONE').name = "Scale"
# 8 - TOP
pie.operator("w.manupulators", text="Show/Hide Toggle", icon='MANIPUL')
pie.operator("w.manupulators", text="Show/Hide Toggle", icon='NONE')
classes = (

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Switch between 3d view object/edit modes",
"author": "pitiwazou, meta-androcto, italic",
"version": (0, 1, 2),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -276,7 +276,7 @@ class PieObjectEditotherModes(Menu):
box.operator("verts.edges", text="Vertex/Edges", icon='VERTEXSEL')
box.operator("verts.edgesfaces", text="Vertex/Edges/Faces", icon='OBJECT_DATAMODE')
box.operator("wm.context_toggle", text="Limit to Visible",
icon="ORTHO").data_path = "space_data.use_occlude_geometry"
icon="NONE").data_path = "space_data.use_occlude_geometry"
class PieObjectEditMode(Menu):

View File

@ -24,7 +24,7 @@ bl_info = {
"name": "Hotkey: 'Alt + Spacebar'",
"author": "Italic_",
"version": (1, 1, 0),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"description": "Set Transform Orientations",
"location": "3D View",
"category": "Orientation Pie"}
@ -51,7 +51,7 @@ class OrientPoll(Operator):
return context.space_data.type == "VIEW_3D"
def execute(self, context):
context.space_data.transform_orientation = self.space
context.scene.transform_orientation = self.space
return {'FINISHED'}

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Origin Snap/Place Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -105,10 +105,10 @@ class PieOriginPivot(Menu):
if obj and obj.type == 'MESH':
# 4 - LEFT
pie.operator("object.origin_set", text="Origin to Center of Mass",
icon='BBOX').type = 'ORIGIN_CENTER_OF_MASS'
icon='NONE').type = 'ORIGIN_CENTER_OF_MASS'
# 6 - RIGHT
pie.operator("object.origin_set", text="Origin To 3D Cursor",
icon='CURSOR').type = 'ORIGIN_CURSOR'
icon='PIVOT_CURSOR').type = 'ORIGIN_CURSOR'
# 2 - BOTTOM
pie.operator("object.pivotobottom", text="Origin to Bottom",
icon='TRIA_DOWN')
@ -117,27 +117,27 @@ class PieOriginPivot(Menu):
icon='SNAP_INCREMENT')
# 7 - TOP - LEFT
pie.operator("object.origin_set", text="Geometry To Origin",
icon='BBOX').type = 'GEOMETRY_ORIGIN'
icon='NONE').type = 'GEOMETRY_ORIGIN'
# 9 - TOP - RIGHT
pie.operator("object.origin_set", text="Origin To Geometry",
icon='ROTATE').type = 'ORIGIN_GEOMETRY'
icon='NONE').type = 'ORIGIN_GEOMETRY'
else:
# 4 - LEFT
pie.operator("object.origin_set", text="Origin to Center of Mass",
icon='BBOX').type = 'ORIGIN_CENTER_OF_MASS'
icon='NONE').type = 'ORIGIN_CENTER_OF_MASS'
# 6 - RIGHT
pie.operator("object.origin_set", text="Origin To 3D Cursor",
icon='CURSOR').type = 'ORIGIN_CURSOR'
icon='PIVOT_CURSOR').type = 'ORIGIN_CURSOR'
# 2 - BOTTOM
pie.operator("object.pivot2selection", text="Origin To Selection",
icon='SNAP_INCREMENT')
# 8 - TOP
pie.operator("object.origin_set", text="Origin To Geometry",
icon='ROTATE').type = 'ORIGIN_GEOMETRY'
icon='NONE').type = 'ORIGIN_GEOMETRY'
# 7 - TOP - LEFT
pie.operator("object.origin_set", text="Geometry To Origin",
icon='BBOX').type = 'GEOMETRY_ORIGIN'
icon='NONE').type = 'GEOMETRY_ORIGIN'
classes = (

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Set Pivot Point Menu",
"author": "seb_k, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Proportional Object/Edit Tools",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View Object & Edit modes",
"warning": "",
"wiki_url": "",

View File

@ -21,7 +21,7 @@
bl_info = {
"name": "Hotkey: 'Ctrl S'",
"description": "Save/Open & File Menus",
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "All Editors",
"warning": "",
"wiki_url": "",
@ -46,17 +46,17 @@ class PieSaveOpen(Menu):
layout = self.layout
pie = layout.menu_pie()
# 4 - LEFT
pie.operator("wm.read_homefile", text="New", icon='NEW')
pie.operator("wm.read_homefile", text="New", icon='FILE_NEW')
# 6 - RIGHT
pie.menu("pie.link", text="Link", icon='LINK_BLEND')
# 2 - BOTTOM
pie.menu("pie.fileio", text="Import/Export Menu", icon='IMPORT')
# 8 - TOP
pie.operator("file.save_incremental", text="Incremental Save", icon='SAVE_COPY')
pie.operator("file.save_incremental", text="Incremental Save", icon='NONE')
# 7 - TOP - LEFT
pie.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
# 9 - TOP - RIGHT
pie.operator("wm.save_as_mainfile", text="Save As...", icon='SAVE_AS')
pie.operator("wm.save_as_mainfile", text="Save As...", icon='NONE')
# 1 - BOTTOM - LEFT
pie.operator("wm.open_mainfile", text="Open file", icon='FILE_FOLDER')
# 3 - BOTTOM - RIGHT
@ -84,7 +84,7 @@ class pie_recover(Menu):
layout = self.layout
pie = layout.menu_pie()
box = pie.split().column()
box.operator("wm.recover_auto_save", text="Recover Auto Save...", icon='RECOVER_AUTO')
box.operator("wm.recover_auto_save", text="Recover Auto Save...", icon='NONE')
box.operator("wm.recover_last_session", text="Recover Last Session", icon='RECOVER_LAST')
box.operator("wm.revert_mainfile", text="Revert", icon='FILE_REFRESH')

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Sculpt Brush Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 0),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "W key",
"warning": "",
"wiki_url": "",
@ -123,7 +123,7 @@ class PieSculptthree(Menu):
layout.operator("paint.brush_select",
text='Snakehook', icon='BRUSH_SNAKE_HOOK').sculpt_tool = 'SNAKE_HOOK'
layout.operator("paint.brush_select",
text='Twist', icon='BRUSH_ROTATE').sculpt_tool = 'ROTATE'
text='Twist', icon='BRUSH_ROTATE').sculpt_tool = 'NONE'
classes = (

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Object/Edit mode Selection Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -44,7 +44,7 @@ class PieSelectionsMore(Menu):
pie = layout.menu_pie()
box = pie.split().column()
box.operator("object.select_by_type", text="Select By Type", icon='SNAP_VOLUME')
box.operator("object.select_grouped", text="Select Grouped", icon='ROTATE')
box.operator("object.select_grouped", text="Select Grouped", icon='NONE')
box.operator("object.select_linked", text="Select Linked", icon='CONSTRAINT_BONE')
box.menu("VIEW3D_MT_select_object_more_less", text="More/Less")
@ -58,7 +58,7 @@ class PieSelectionsOM(Menu):
layout = self.layout
pie = layout.menu_pie()
# 4 - LEFT
pie.operator("object.select_by_layer", text="Select By Layer", icon='LAYER_ACTIVE')
pie.row().label(text="")
# 6 - RIGHT
pie.operator("object.select_random", text="Select Random", icon='GROUP_VERTEX')
# 2 - BOTTOM
@ -66,11 +66,11 @@ class PieSelectionsOM(Menu):
icon='ZOOM_PREVIOUS').action = 'INVERT'
# 8 - TOP
pie.operator("object.select_all", text="Select All Toggle",
icon='RENDER_REGION').action = 'TOGGLE'
icon='NONE').action = 'TOGGLE'
# 7 - TOP - LEFT
pie.operator("view3d.select_circle", text="Circle Select", icon='BORDER_LASSO')
pie.operator("view3d.select_circle", text="Circle Select", icon='NONE')
# 9 - TOP - RIGHT
pie.operator("view3d.select_box", text="Box Select", icon='BORDER_RECT')
pie.operator("view3d.select_box", text="Box Select", icon='NONE')
# 1 - BOTTOM - LEFT
pie.operator("object.select_camera", text="Select Camera", icon='CAMERA_DATA')
# 3 - BOTTOM - RIGHT
@ -87,7 +87,7 @@ class PieSelectionsEM(Menu):
pie = layout.menu_pie()
# 4 - LEFT
pie.operator("view3d.select_box", text="Box Select",
icon='BORDER_RECT')
icon='NONE')
# 6 - RIGHT
pie.menu("object.selectloopselection", text="Select Loop Menu", icon='LOOPSEL')
# 2 - BOTTOM
@ -104,7 +104,7 @@ class PieSelectionsEM(Menu):
icon='FULLSCREEN_EXIT').action = 'INVERT'
# 1 - BOTTOM - LEFT
pie.operator("view3d.select_circle", text="Circle Select",
icon='BORDER_LASSO')
icon='NONE')
# 3 - BOTTOM - RIGHT
pie.menu("object.selectallbyselection", text="Multi Select Menu", icon='SNAP_EDGE')

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Viewport Shading Menus",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
@ -43,7 +43,7 @@ class PieShadingView(Menu):
layout = self.layout
pie = layout.menu_pie()
pie.prop(context.space_data, "viewport_shade", expand=True)
pie.prop(context.space_data.shading, "type", expand=True)
if context.active_object:
if context.mode == 'EDIT_MESH':

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Snap Element Menu",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "3d View",
"warning": "",
"wiki_url": "",
@ -60,7 +60,7 @@ class PieSnaping(Menu):
# 1 - BOTTOM - LEFT
pie.operator("snap.alignrotation", text="Align rotation", icon='SNAP_NORMAL')
# 3 - BOTTOM - RIGHT
pie.operator("wm.call_menu_pie", text="Snap Target", icon='SNAP_SURFACE').name = "snap.targetmenu"
pie.operator("wm.call_menu_pie", text="Snap Target", icon='NONE').name = "snap.targetmenu"
class SnapActive(Operator):

View File

@ -23,7 +23,7 @@ bl_info = {
"description": "Viewport Numpad Menus",
"author": "pitiwazou, meta-androcto",
"version": (0, 1, 1),
"blender": (2, 77, 0),
"blender": (2, 80, 0),
"location": "Q key",
"warning": "",
"wiki_url": "",
@ -108,17 +108,17 @@ class PieViewNumpad(Menu):
rd = scene.render
# 4 - LEFT
pie.operator("view3d.viewnumpad", text="Left", icon='TRIA_LEFT').type = 'LEFT'
pie.operator("view3d.view_axis", text="Left", icon='TRIA_LEFT').type = 'LEFT'
# 6 - RIGHT
pie.operator("view3d.viewnumpad", text="Right", icon='TRIA_RIGHT').type = 'RIGHT'
pie.operator("view3d.view_axis", text="Right", icon='TRIA_RIGHT').type = 'RIGHT'
# 2 - BOTTOM
pie.operator("view3d.viewnumpad", text="Bottom", icon='TRIA_DOWN').type = 'BOTTOM'
pie.operator("view3d.view_axis", text="Bottom", icon='TRIA_DOWN').type = 'BOTTOM'
# 8 - TOP
pie.operator("view3d.viewnumpad", text="Top", icon='TRIA_UP').type = 'TOP'
pie.operator("view3d.view_axis", text="Top", icon='TRIA_UP').type = 'TOP'
# 7 - TOP - LEFT
pie.operator("view3d.viewnumpad", text="Front").type = 'FRONT'
pie.operator("view3d.view_axis", text="Front").type = 'FRONT'
# 9 - TOP - RIGHT
pie.operator("view3d.viewnumpad", text="Back").type = 'BACK'
pie.operator("view3d.view_axis", text="Back").type = 'BACK'
# 1 - BOTTOM - LEFT
box = pie.split().column()
row = box.row(align=True)
@ -131,8 +131,8 @@ class PieViewNumpad(Menu):
icon='LOCKED').data_path = "space_data.lock_camera"
row = box.row(align=True)
row.operator("view3d.viewnumpad", text="View Cam", icon='VISIBLE_IPO_ON').type = 'CAMERA'
row.operator("view3d.camera_to_view", text="Cam to view", icon='MAN_TRANS')
row.operator("view3d.view_camera", text="View Cam", icon='VISIBLE_IPO_ON')
row.operator("view3d.camera_to_view", text="Cam to view", icon='NONE')
icon_locked = 'LOCKED' if ob and ob.lock_rotation[0] is False else \
'UNLOCKED' if ob and ob.lock_rotation[0] is True else 'LOCKED'
@ -143,7 +143,7 @@ class PieViewNumpad(Menu):
row = box.row(align=True)
row.prop(rd, "use_border", text="Border")
# 3 - BOTTOM - RIGHT
pie.menu(PieViewallSelGlobEtc.bl_idname, text="View Menu", icon='BBOX')
pie.menu(PieViewallSelGlobEtc.bl_idname, text="View Menu", icon='NONE')
classes = (

View File

@ -67,7 +67,7 @@ class VIEW3D_MT_Space_Dynamic_Menu(Menu):
layout.menu("VIEW3D_MT_View_Navigation", icon='PIVOT_BOUNDBOX')
layout.menu("VIEW3D_MT_View_Toggle", icon='WORKSPACE')
layout.operator("view3d.snap_cursor_to_center",
text="Cursor to Center")
text="Cursor to World Origin")
layout.operator("view3d.snap_cursor_to_grid",
text="Cursor to Grid")
layout.menu("VIEW3D_MT_UndoS", icon='ARROW_LEFTRIGHT')
@ -974,7 +974,7 @@ class VIEW3D_MT_CursorMenu(Menu):
layout.operator("view3d.snap_cursor_to_selected",
text="Cursor to Selected")
layout.operator("view3d.snap_cursor_to_center",
text="Cursor to Center")
text="Cursor to World Origin")
layout.operator("view3d.snap_cursor_to_grid",
text="Cursor to Grid")
layout.operator("view3d.snap_cursor_to_active",
@ -987,7 +987,7 @@ class VIEW3D_MT_CursorMenu(Menu):
layout.operator("view3d.snap_selected_to_grid",
text="Selection to Grid")
layout.operator("view3d.snap_cursor_selected_to_center",
text="Selection and Cursor to Center")
text="Selection and Cursor to World Origin")
UseSeparator(self, context)
layout.menu("VIEW3D_MT_Pivot")
layout.operator("view3d.pivot_cursor",
@ -1007,7 +1007,7 @@ class VIEW3D_MT_CursorMenuLite(Menu):
layout.operator("view3d.snap_cursor_to_selected",
text="Cursor to Selected")
layout.operator("view3d.snap_cursor_to_center",
text="Cursor to Center")
text="Cursor to World Origin")
layout.operator("view3d.snap_cursor_to_grid",
text="Cursor to Grid")
layout.operator("view3d.snap_cursor_to_active",
@ -1020,7 +1020,7 @@ class VIEW3D_MT_CursorMenuLite(Menu):
layout.operator("view3d.snap_selected_to_grid",
text="Selection to Grid")
layout.operator("view3d.snap_cursor_selected_to_center",
text="Selection and Cursor to Center")
text="Selection and Cursor to World Origin")
UseSeparator(self, context)
layout.menu("VIEW3D_MT_Pivot")
layout.operator("view3d.pivot_cursor",
@ -1415,7 +1415,7 @@ class VIEW3D_MT_EditCursorMenu(Menu):
layout.operator("view3d.snap_cursor_to_selected",
text="Cursor to Selected")
layout.operator("view3d.snap_cursor_to_center",
text="Cursor to Center")
text="Cursor to World Origin")
layout.operator("view3d.snap_cursor_to_grid",
text="Cursor to Grid")
layout.operator("view3d.snap_cursor_to_active",
@ -2926,7 +2926,7 @@ class SetOriginToSelected(Operator):
# Code thanks to Isaac Weaver (wisaac) D1963
class SnapCursSelToCenter(Operator):
bl_idname = "view3d.snap_cursor_selected_to_center"
bl_label = "Snap Cursor & Selection to Center"
bl_label = "Snap Cursor & Selection to World Origin"
bl_description = ("Snap 3D cursor and selected objects to the center \n"
"Works only in Object Mode")

View File

@ -28,7 +28,8 @@ bl_info = {
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/System/Demo_Mode#Running_Demo_Mode",
"support": 'OFFICIAL',
"category": "System"}
"category": "System",
}
# To support reload properly, try to access a package var, if it's there, reload everything
if "bpy" in locals():
@ -70,9 +71,11 @@ class DemoModeSetup(bpy.types.Operator):
)
mode: EnumProperty(
name="Method",
items=(('AUTO', "Auto", ""),
('PLAY', "Play", ""),
('RENDER', "Render", ""))
items=(
('AUTO', "Auto", ""),
('PLAY', "Play", ""),
('RENDER', "Render", ""),
)
)
run: BoolProperty(
@ -243,9 +246,15 @@ def menu_func(self, context):
layout.separator()
classes = (
DemoModeSetup,
DemoModeRun,
)
def register():
bpy.utils.register_class(DemoModeSetup)
bpy.utils.register_class(DemoModeRun)
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.TOPBAR_MT_file.prepend(menu_func)
@ -253,8 +262,9 @@ def register():
def unregister():
bpy.utils.unregister_class(DemoModeSetup)
bpy.utils.unregister_class(DemoModeRun)
from bpy.utils import unregister_class
for cls in classes:
unregister_class(cls)
bpy.types.TOPBAR_MT_file.remove(menu_func)

View File

@ -65,7 +65,7 @@ def as_string(dirpath, random_order, exit, **kwargs):
"\n",
"exit = %r\n" % exit,
"\n",
]
]
# All these work but use nicest formatting!
if 0: # works but not nice to edit.

View File

@ -44,23 +44,26 @@ DEMO_CFG = "demo.py"
# populate from script
global_config_files = []
global_config = dict(anim_cycles=1,
anim_render=False,
anim_screen_switch=0.0,
anim_time_max=60.0,
anim_time_min=4.0,
mode='AUTO',
display_render=4.0)
global_config = dict(
anim_cycles=1,
anim_render=False,
anim_screen_switch=0.0,
anim_time_max=60.0,
anim_time_min=4.0,
mode='AUTO',
display_render=4.0,
)
# switch to the next file in 2 sec.
global_config_fallback = dict(anim_cycles=1,
anim_render=False,
anim_screen_switch=0.0,
anim_time_max=60.0,
anim_time_min=4.0,
mode='AUTO',
display_render=4.0)
global_config_fallback = dict(
anim_cycles=1,
anim_render=False,
anim_screen_switch=0.0,
anim_time_max=60.0,
anim_time_min=4.0,
mode='AUTO',
display_render=4.0,
)
global_state = {
"init_time": 0.0,
@ -253,9 +256,10 @@ def demo_mode_update():
demo_mode_next_file()
return
# above cycles and minimum display time
if (time_total > global_config["anim_time_min"]) and \
(global_state["anim_cycles"] > global_config["anim_cycles"]):
if (
(time_total > global_config["anim_time_min"]) and
(global_state["anim_cycles"] > global_config["anim_cycles"])
):
# looped enough now.
demo_mode_next_file()
return