diff --git a/citation.txt b/citation.txt
new file mode 100644
index 0000000..a1d821a
--- /dev/null
+++ b/citation.txt
@@ -0,0 +1,31 @@
+Original Papers First introduced by Chen and Medioni (1991) and Besl and McKay (1992).
+
+
+1. Chen, Yang; Gerard Medioni (1991).
+ "Object modelling by registration of multiple range images".
+ Image Vision Comput.
+ 10 (3): 145–155. doi:10.1016/0262-8856(92)90066-C.
+
+2. Besl, Paul J.; N.D. McKay (1992).
+ "A Method for Registration of 3-D Shapes".
+ IEEE Transactions on Pattern Analysis and Machine Intelligence.
+ 14 (2): 239–256. doi:10.1109/34.121791.
+
+3.Transformations Python Library
+Author: Christoph Gohlke `_
+Organization: Laboratory for Fluorescence Dynamics. University of California, Irvine
+https://www.lfd.uci.edu/~gohlke/code/transformations.py.html
+
+4. FINDING OPTIMAL ROTATION AND TRANSLATION BETWEEN CORRESPONDING 3D POINTS
+Author: Nghia Ho, nghiaho12@yahoo.com
+Organization:
+https://nghiaho.com/?page_id=671
+http://nghiaho.com/uploads/code/rigid_transform_3D.py
+
+5. Blender Implementation, Filtering Code, UI, BVH Acceleration
+Modal Operator
+Patrick Moore: patrick.moore.bu@gmail.com
+working as a hobby and later update for D3tool.com
+
+
+### Add Additional Citation Below ###
\ No newline at end of file
diff --git a/functions/general.py b/functions/general.py
index f0eb385..e9266af 100644
--- a/functions/general.py
+++ b/functions/general.py
@@ -273,7 +273,7 @@ def make_pairs(align_obj, base_obj, base_bvh, vlist, thresh, sample = 0, calc_st
vert = align_obj.data.vertices[vert_ind]
#closest point for point clouds. Local space of base obj
- co_find = imx2 * (mx1 * vert.co)
+ co_find = imx2 @ (mx1 @ vert.co)
#closest surface point for triangle mesh
#this is set up for a well modeled aligning object with
@@ -285,12 +285,12 @@ def make_pairs(align_obj, base_obj, base_bvh, vlist, thresh, sample = 0, calc_st
#res, co1, normal, face_index = base_obj.closest_point_on_mesh(co_find)
co1, n, face_index, d = base_bvh.find_nearest(co_find)
- dist = (mx2 * co_find - mx2 * co1).length
+ dist = (mx2 @ co_find - mx2 @ co1).length
#d is now returned by bvh.find
#dist = mx2.to_scale() * d
if face_index != -1 and dist < thresh:
verts1.append(vert.co)
- verts2.append(imx1 * (mx2 * co1))
+ verts2.append(imx1 @ (mx2 @ co1))
if calc_stats:
dists.append(dist)
diff --git a/functions/utilities.py b/functions/utilities.py
index 26df1e1..49342f1 100644
--- a/functions/utilities.py
+++ b/functions/utilities.py
@@ -71,7 +71,7 @@ def draw_3d_points_revised(context, points, color, size):
for vec in points:
- 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)
@@ -87,7 +87,7 @@ def draw_3d_text(context, font_id, text, vec):
region_mid_height = region.height / 2.0
perspective_matrix = region3d.perspective_matrix.copy()
- 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)
@@ -117,8 +117,8 @@ def get_ray_origin(ray_origin, ray_direction, ob):
if abs(ray_direction.x)>0.0001: planes += [(bm,x), (bM,-x)]
if abs(ray_direction.y)>0.0001: planes += [(bm,y), (bM,-y)]
if abs(ray_direction.z)>0.0001: planes += [(bm,z), (bM,-z)]
- dists = [get_ray_plane_intersection(ray_origin,ray_direction,mx*p0,q*no) for p0,no in planes]
- return ray_origin + ray_direction * min(dists)
+ dists = [get_ray_plane_intersection(ray_origin,ray_direction,mx@p0,q@no) for p0,no in planes]
+ return ray_origin + ray_direction @ min(dists)
# Jon Denning for Retopoflow
def ray_cast_region2d(region, rv3d, screen_coord, obj):
@@ -139,7 +139,7 @@ def ray_cast_region2d(region, rv3d, screen_coord, obj):
bver = '%03d.%03d.%03d' % (bpy.app.version[0],bpy.app.version[1],bpy.app.version[2])
if (bver < '002.072.000') and not rv3d.is_perspective: mult *= -1
- st, en = imx*(o-mult*back*d), imx*(o+mult*d)
+ st, en = imx@(o-mult*back*d), imx@(o+mult*d)
if bversion() < '002.077.000':
hit = obj.ray_cast(st,en)
diff --git a/operators/align_pick_points.py b/operators/align_pick_points.py
index ad9dc34..8f52e07 100644
--- a/operators/align_pick_points.py
+++ b/operators/align_pick_points.py
@@ -14,6 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+#https://cognitivewaves.wordpress.com/opengl-vbo-shader-vao/#shader-with-vertex-buffer-object
# System imports
import time
@@ -22,6 +23,12 @@
# Blender imports
import bpy
import blf
+import bgl
+import gpu
+
+from gpu_extras.batch import batch_for_shader
+
+
from bpy.types import Operator
from mathutils import Matrix
from bpy_extras import view3d_utils
@@ -38,24 +45,49 @@ def draw_callback_px(self, context):
y = context.region.height
dims = blf.dimensions(0, 'A')
- blf.position(font_id, 10, y - 20 - dims[1], 0)
+ #blf.position(font_id, 10, y - 20 - dims[1], 0)
+ blf.position(font_id, 10, 20 + dims[1], 0)
+
blf.size(font_id, 20, 72)
if context.area.x == self.area_align.x:
blf.draw(font_id, "Align: "+ self.align_msg)
- points = [self.obj_align.matrix_world * p for p in self.align_points]
+ points = [self.obj_align.matrix_world @ p for p in self.align_points]
color = (1,0,0,1)
else:
blf.draw(font_id, "Base: " + self.base_msg)
- points = [self.obj_align.matrix_world * p for p in self.base_points]
+ points = [self.obj_align.matrix_world @ p for p in self.base_points]
color = (0,1,0,1)
- draw_3d_points_revised(context, points, color, 4)
+ #draw_3d_points_revised(context, points, color, 4)
for i, vec in enumerate(points):
ind = str(i)
draw_3d_text(context, font_id, ind, vec)
+
+def draw_callback_view(self, context):
+ bgl.glPointSize(8)
+ #print('draw view!')
+ if context.area.x == self.area_align.x:
+ if not self.align_shader:
+ return
+
+ self.align_shader.bind()
+ self.align_shader.uniform_float("color", (1,0,1,1))
+ self.align_batch.draw(self.align_shader)
+ else:
+ if not self.base_shader:
+ return
+ self.base_shader.bind()
+ self.base_shader.uniform_float("color", (1,1,0,1))
+ self.base_batch.draw(self.base_shader)
+
+ bgl.glPointSize(1)
+ pass
+
+
+
class OBJECT_OT_align_pick_points(Operator):
"""Align two objects with 3 or more pair of picked points"""
bl_idname = "object.align_picked_points"
@@ -120,7 +152,7 @@ def modal(self, context, event):
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
- ray_target = ray_origin + (view_vector * ray_max)
+ ray_target = ray_origin + (ray_max * view_vector)
print('in the align object window')
(d, (ok,hit, normal, face_index)) = ray_cast_region2d(region, rv3d, coord, self.obj_align)
@@ -128,6 +160,7 @@ def modal(self, context, event):
print('hit! align_obj %s' % self.obj_align.name)
#local space of align object
self.align_points.append(hit)
+ self.create_batch_align()
else:
@@ -141,15 +174,15 @@ def modal(self, context, event):
coord = (event.mouse_x - region.x, event.mouse_y - region.y)
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
- ray_target = ray_origin + (view_vector * ray_max)
+ ray_target = ray_origin + (ray_max * view_vector)
print('in the base object window')
(d, (ok,hit, normal, face_index)) = ray_cast_region2d(region, rv3d, coord, self.obj_base)
if ok:
print('hit! base_obj %s' % self.obj_base.name)
#points in local space of align object
- self.base_points.append(self.obj_align.matrix_world.inverted() * self.obj_base.matrix_world * hit)
-
+ self.base_points.append(self.obj_align.matrix_world.inverted() @ self.obj_base.matrix_world @ hit)
+ self.create_batch_base()
return {'RUNNING_MODAL'}
@@ -157,8 +190,10 @@ def modal(self, context, event):
if event.mouse_x > self.area_align.x and event.mouse_x < self.area_align.x + self.area_align.width:
self.align_points.pop()
+ self.create_batch_align()
else:
self.base_points.pop()
+ self.create_batch_base()
return {'RUNNING_MODAL'}
@@ -201,18 +236,20 @@ def modal(self, context, event):
return {'PASS_THROUGH'}
elif event.type in {'ESC'}:
- bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
+ bpy.types.SpaceView3D.draw_handler_remove(self._2Dhandle, 'WINDOW')
+ bpy.types.SpaceView3D.draw_handler_remove(self._3Dhandle, 'WINDOW')
return {'CANCELLED'}
elif event.type == 'RET':
if len(self.align_points) >= 3 and len(self.base_points) >= 3 and len(self.align_points) == len(self.base_points):
- bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
+ bpy.types.SpaceView3D.draw_handler_remove(self._2Dhandle, 'WINDOW')
+ bpy.types.SpaceView3D.draw_handler_remove(self._3Dhandle, 'WINDOW')
self.de_localize(context)
self.align_obj(context)
- context.scene.objects.active = self.obj_align
- self.obj_align.select = True
+ context.view_layer.objects.active = self.obj_align
+ self.obj_align.select_set(True)
self.obj_base = True
return {'FINISHED'}
@@ -238,9 +275,9 @@ def invoke(self, context, event):
obj2_name = [obj for obj in context.selected_objects if obj != context.object][0].name
for ob in context.scene.objects:
- ob.select = False
+ ob.select_set(False)
- context.scene.objects.active = None
+ bpy.context.view_layer.objects.active= None#context.scene.objects.active = None
#I did this stupid method becuase I was unsure
#if some things were being "sticky" and not
@@ -249,7 +286,7 @@ def invoke(self, context, event):
obj2 = bpy.data.objects[obj2_name]
for ob in bpy.data.objects:
- if ob.select:
+ if ob.select_set(True):
print(ob.name)
screen = context.window.screen
@@ -258,31 +295,43 @@ def invoke(self, context, event):
if area.type == 'VIEW_3D':
break
- bpy.ops.view3d.toolshelf() #close the first toolshelf
+ #bpy.ops.view3d.toolshelf() #close the first toolshelf
override = context.copy()
override['area'] = area
self.area_align = area
- bpy.ops.screen.area_split(override, direction='VERTICAL', factor=0.5, mouse_x=-100, mouse_y=-100)
+ bpy.ops.screen.area_split(direction='VERTICAL', factor=0.5, cursor=(100,-100))#bpy.ops.screen.area_split(override, direction='VERTICAL', factor=0.5, mouse_x=-100, mouse_y=-100)
#bpy.ops.view3d.toolshelf() #close the 2nd toolshelf
+
- context.scene.objects.active = obj1
- obj1.select = True
- obj2.select = False
+ bpy.context.view_layer.objects.active = obj1
+ obj1.select_set(True)
+ obj2.select_set(False)
bpy.ops.view3d.localview(override)
+
+
+ #..........Hide sidebar after area split...........................
+ for A in bpy.context.screen.areas:
+ if A.type == 'VIEW_3D' :
+ ctx = bpy.context.copy()
+ ctx['area'] = A
+ bpy.ops.screen.region_toggle(ctx, region_type='UI')
+#...................................................................
+
+
- obj1.select = False
- context.scene.objects.active = None
+ obj1.select_set(False)
+ bpy.context.view_layer.objects.active = None
override = context.copy()
for area in screen.areas:
if area.as_pointer() not in areas:
override['area'] = area
self.area_base = area
bpy.ops.object.select_all(action = 'DESELECT')
- context.scene.objects.active = obj2
- obj2.select = True
+ bpy.context.view_layer.objects.active = obj2
+ obj2.select_set(True)
override['selected_objects'] = [obj2]
override['selected_editable_objects'] = [obj2]
override['object'] = obj2
@@ -298,13 +347,34 @@ def invoke(self, context, event):
self.align_points = []
self.base_points = []
+ self.base_batch = None
+ self.base_shader = None
+ self.align_batch = None
+ self.align_shader = None
context.window_manager.modal_handler_add(self)
- self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
+ self._2Dhandle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
+ self._3Dhandle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_view, (self, context), 'WINDOW', 'POST_VIEW')
+
+
return {'RUNNING_MODAL'}
#############################################
# class methods
+
+ def create_batch_base(self):
+ verts = [self.obj_align.matrix_world @ p for p in self.base_points]
+ vertices = [(v.x, v.y, v.z) for v in verts]
+ self.base_shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
+ self.base_batch = batch_for_shader(self.base_shader, 'POINTS', {"pos":vertices})
+
+
+ def create_batch_align(self):
+ verts = [self.obj_align.matrix_world @ p for p in self.align_points]
+ vertices = [(v.x, v.y, v.z) for v in verts]
+ self.align_shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
+ self.align_batch = batch_for_shader(self.base_shader, 'POINTS', {"pos":vertices})
+
def de_localize(self,context):
override = context.copy()
@@ -316,10 +386,15 @@ def de_localize(self,context):
bpy.ops.view3d.localview(override)
bpy.ops.view3d.view_selected(override)
- #Crash Blender?
- bpy.ops.screen.area_join(min_x=self.area_align.x,min_y=self.area_align.y, max_x=self.area_base.x, max_y=self.area_base.y)
- bpy.ops.view3d.toolshelf()
+#............Crash Blender? Resolve................................
+ xj = int(self.area_align.width + 1)
+ yj = int(self.area_align.y + self.area_align.height / 2)
+ bpy.ops.screen.area_join(cursor=(xj,yj))
+#..................................................................
+ #bpy.ops.view3d.toolshelf()
+ bpy.ops.screen.screen_full_area()
+ bpy.ops.screen.screen_full_area()
#ret = bpy.ops.screen.area_join(min_x=area_base.x,min_y=area_base.y, max_x=area_align.x, max_y=area_align.y)
def align_obj(self,context):
@@ -361,7 +436,7 @@ def align_obj(self,context):
#because we calced transform in local space
#it's this easy to update the obj...
- self.obj_align.matrix_world = self.obj_align.matrix_world * new_mat
+ self.obj_align.matrix_world = self.obj_align.matrix_world @ new_mat
self.obj_align.update_tag()
- context.scene.update()
+ context.view_layer.update()
diff --git a/operators/icp_align.py b/operators/icp_align.py
index 723ab4d..39cc178 100644
--- a/operators/icp_align.py
+++ b/operators/icp_align.py
@@ -41,8 +41,8 @@ class OBJECT_OT_icp_align(Operator):
@classmethod
def poll(cls, context):
condition_1 = len(context.selected_objects) == 2
- conidion_2 = context.object.type == 'MESH'
- return condition_1 and condition_1
+ condition_2 = context.object.type == 'MESH'
+ return condition_1 and condition_2
def execute(self, context):
settings = get_addon_preferences()
@@ -50,7 +50,7 @@ def execute(self, context):
start = time.time()
align_obj = context.object
base_obj = [obj for obj in context.selected_objects if obj != align_obj][0]
- base_bvh = BVHTree.FromObject(base_obj, context.scene)
+ base_bvh = BVHTree.FromObject(base_obj, context.evaluated_depsgraph_get())
align_obj.rotation_mode = 'QUATERNION'
vlist = []
@@ -108,12 +108,13 @@ def execute(self, context):
for z in range(0,4):
new_mat[y][z] = M[y][z]
- align_obj.matrix_world = align_obj.matrix_world * new_mat
+ align_obj.matrix_world = align_obj.matrix_world @ new_mat
trans = new_mat.to_translation()
quat = new_mat.to_quaternion()
align_obj.update_tag()
- context.scene.update()
+ context.view_layer.update()
+ #context.scene.update()
if d_stats:
i = int(fmod(n,5))
diff --git a/operators/icp_align_feedback.py b/operators/icp_align_feedback.py
index 5145c8f..8615c3c 100644
--- a/operators/icp_align_feedback.py
+++ b/operators/icp_align_feedback.py
@@ -46,7 +46,7 @@ def poll(cls, context):
def invoke(self,context, event):
wm = context.window_manager
- self._timer = wm.event_timer_add(0.01, context.window)
+ self._timer = wm.event_timer_add(time_step = 0.01, window = context.window)
wm.modal_handler_add(self)
settings = get_addon_preferences()
@@ -54,7 +54,7 @@ def invoke(self,context, event):
self.start = time.time()
self.align_obj = context.object
self.base_obj = [obj for obj in context.selected_objects if obj != self.align_obj][0]
- self.base_bvh = BVHTree.FromObject(self.base_obj, context.scene)
+ self.base_bvh = BVHTree.FromObject(self.base_obj, context.evaluated_depsgraph_get())
self.align_obj.rotation_mode = 'QUATERNION'
self.vlist = []
@@ -132,7 +132,7 @@ def execute(self, context):
start = time.time()
align_obj = context.object
base_obj = [obj for obj in context.selected_objects if obj != align_obj][0]
- base_bvh = BVHTree.FromObject(base_obj, context.scene)
+ base_bvh = BVHTree.FromObject(base_obj, context.evaluated_depsgraph_get())
align_obj.rotation_mode = 'QUATERNION'
vlist = []
@@ -190,12 +190,13 @@ def execute(self, context):
for z in range(0,4):
new_mat[y][z] = M[y][z]
- align_obj.matrix_world = align_obj.matrix_world * new_mat
+ align_obj.matrix_world = align_obj.matrix_world @ new_mat
trans = new_mat.to_translation()
quat = new_mat.to_quaternion()
align_obj.update_tag()
- context.scene.update()
+ #context.scene.update()
+ context.view_layer.update()
if d_stats:
i = int(fmod(n,5))
@@ -252,7 +253,7 @@ def iterate(self,context):
for z in range(0,4):
new_mat[y][z] = M[y][z]
- self.align_obj.matrix_world = self.align_obj.matrix_world * new_mat
+ self.align_obj.matrix_world = self.align_obj.matrix_world @ new_mat
trans = new_mat.to_translation()
quat = new_mat.to_quaternion()