Merge branch 'blender2.8' of git.blender.org:blender-addons into blender2.8
This commit is contained in:
commit
2bb579b798
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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']))
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'},
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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
|
@ -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()
|
|
@ -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'}
|
|
@ -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.
|
@ -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()
|
|
@ -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")
|
|
@ -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__()
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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')
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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:
|
||||
|
|
|
@ -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="")
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
|
@ -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'}
|
||||
|
|
|
@ -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()):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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)
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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": "",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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": "",
|
||||
|
|
|
@ -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": "",
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue