From 8e3ab3f19d740e20d6d6c11e697b58ed18ce4cea Mon Sep 17 00:00:00 2001 From: poikilos <7557867+poikilos@users.noreply.github.com> Date: Wed, 2 Oct 2019 14:34:34 -0400 Subject: [PATCH] collisionbox: add Minetest h symmetry, enable by default --- .../blender/generate_lua_collisionbox.py | 158 +++++++++++------- 1 file changed, 97 insertions(+), 61 deletions(-) diff --git a/utilities/blender/generate_lua_collisionbox.py b/utilities/blender/generate_lua_collisionbox.py index 550ff02..381c07c 100644 --- a/utilities/blender/generate_lua_collisionbox.py +++ b/utilities/blender/generate_lua_collisionbox.py @@ -2,8 +2,19 @@ print("How to use: paste into a Blender Text Editor panel, select" " object, Run Script") y_up = True -enable_minetest = False - +enable_minetest = True +enable_lowest_h = False +enable_center_h = False +if enable_minetest: + enable_lowest_h = True + enable_center_h = True + +hs = (0, 1) # horizontal axis indices +v = 2 # vertical axis index +# Do NOT swap until end. +#if y_up: +# hs = (0, 2) +# v = 1 import bpy # from mathutils import Matrix @@ -54,7 +65,7 @@ if ob1 is not None: if ob1 is None: msg = "Nothing is selected." bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) -if (mesh is not None) and (not hasattr(mesh, 'vertices')): +elif (mesh is not None) and (not hasattr(mesh, 'vertices')): msg = "Collision box for armatures cannot be calculated." bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) else: @@ -64,26 +75,14 @@ else: # See https://blender.stackexchange.com/questions/8459/get-blender-x-y-z-and-bounding-box-with-script # bbox_corners = [ob1.matrix_world * Vector(corner) for corner in ob1.bound_box] - # use ground as bottom (don't do this--it is not the Minetest way) - # if zMin < 0.0: - # yMax -= yMin - # yMin = 0.0 - - # print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}," - # " {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, zMax)) # See https://blender.stackexchange.com/questions/6139/how-to-iterate-through-all-vertices-of-an-object-that-contains-multiple-meshes # print("mesh:" + str(mesh)) # print("hasattr(mesh, 'vertices'):" # + str(hasattr(mesh, 'vertices')))] - xMin = None # Define so None check is possible later. + mins = [None, None, None] # minimums; in outer scope for checks. + maxes = [None, None, None] # minimums; in outer scope for checks. if mesh is not None: - xMin = None - yMin = None - zMin = None - xMax = None - yMax = None - zMax = None wm = ob1.matrix_world for vert in mesh.vertices: # This matrix multiplication is NOT transitive. @@ -91,24 +90,18 @@ else: loc = wm @ vert.co except TypeError: loc = wm * vert.co # Blender <2.8 - # switch y and z for Minetest (y-up) - if (xMin is None) or (loc.x < xMin): - xMin = loc.x - if (xMax is None) or (loc.x > xMax): - xMax = loc.x - if (yMin is None) or (loc.y < yMin): - yMin = loc.y - if (yMax is None) or (loc.y > yMax): - yMax = loc.y - if (zMin is None) or (loc.z < zMin): - zMin = loc.z - if (zMax is None) or (loc.z > zMax): - zMax = loc.z + # NOTE: swap y and z for Minetest (y-up) LATER + coords = (loc.x, loc.y, loc.z) + for i in range(3): + if (mins[i] is None) or (coords[i] < mins[i]): + mins[i] = coords[i] + if (maxes[i] is None) or (coords[i] > maxes[i]): + maxes[i] = coords[i] # print(str(extents1)) # print("--by vertices (raw):") - print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}," - " {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, - zMax)) + # print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}," + # " {:.2f}, {:.2f}}}".format(mins[0], mins[2], mins[1], + # maxes[0], maxes[2], maxes[1])) # Use ob1.matrix_world (above) instead of incrementing # ob1.location.x, y, and z @@ -124,14 +117,14 @@ else: # except TypeError: # loc = wm * vert.co # Blender <2.8 # isFar = False - # if loc.x == xMax or loc.y == yMax or loc.z == zMax: + # if loc.x == maxes[0] or loc.y == maxes[1] or loc.z == maxes[2]: # isFar = True - # elif loc.x == xMin or loc.y == yMin or loc.z == zMin: + # elif loc.x == mins[0] or loc.y == mins[1] or loc.z == mins[2]: # isFar = True # if isFar: # pass # # result = bpy.ops.object.add(type='EMPTY', radius=.25, - # # location=loc); + # # location=loc) # # NOTE: result is merely {'FINISHED'} # # print("{:.2f}, {:.2f}, {:.2f}".format(loc.x, loc.y, # # loc.z)) @@ -142,40 +135,83 @@ else: else: extents1 = ob1.scale.copy() # Object is an empty, so scale up for Minetest - extents1.x = extents1.x * 2.0 - extents1.y = extents1.y * 2.0 - extents1.z = extents1.z * 2.0 - xMin = obj1Loc.x - extents1.x / 2.0 - xMax = obj1Loc.x + extents1.x / 2.0 - yMin = obj1Loc.y - extents1.y / 2.0 - yMax = obj1Loc.y + extents1.y / 2.0 - zMin = obj1Loc.z - extents1.z / 2.0 - zMax = obj1Loc.z + extents1.z / 2.0 + extents1.x = extents1.x * (ob1.empty_draw_size * 2.0) + extents1.y = extents1.y * (ob1.empty_draw_size * 2.0) + extents1.z = extents1.z * (ob1.empty_draw_size * 2.0) + mins[0] = obj1Loc.x - extents1.x / 2.0 + maxes[0] = obj1Loc.x + extents1.x / 2.0 + mins[1] = obj1Loc.y - extents1.y / 2.0 + maxes[1] = obj1Loc.y + extents1.y / 2.0 + mins[2] = obj1Loc.z - extents1.z / 2.0 + maxes[2] = obj1Loc.z + extents1.z / 2.0 msgSuffix = " (using Empty object's scale)" # print("--using empty object:") - # switch y and z for Minetest (y-up): + + # use ground as bottom (don't do this--it is not the Minetest way) + # if mins[2] < 0.0: + # maxes[1] -= mins[1] + # mins[1] = 0.0 + + # print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}," + # " {:.2f}}}".format(mins[0], mins[1], mins[2], maxes[0], maxes[1], maxes[2])) + sizes = [None, None, None] + centers = [None, None, None] + for i in range(3): + sizes[i] = maxes[i] - mins[i] + centers[i] = mins[i] + sizes[i] / 2.0 + + if enable_lowest_h: + # OK to use z as up, since will y&z will be swapped if y_up + hSize = None + for i in range(len(hs)): + axis_i = hs[i] + if (hSize is None) or (sizes[axis_i] < hSize): + hSize = sizes[axis_i] + for i in range(len(hs)): + axis_i = hs[i] + sizes[axis_i] = hSize + mins[axis_i] = centers[axis_i] - hSize / 2 + maxes[axis_i] = mins[axis_i] + hSize + + if enable_center_h: + for i in range(len(hs)): + axis_i = hs[i] + centers[i] = 0 + mins[axis_i] = centers[axis_i] - sizes[axis_i] / 2 + maxes[axis_i] = mins[axis_i] + sizes[axis_i] + + loc = (centers[0], centers[1], centers[2]) + bpy.ops.object.add(type='EMPTY', radius=.5, location=loc) + collisionboxName = "Empty.collisionbox." + ob1.name + newEmpty = bpy.context.scene.objects.active + newEmpty.name = collisionboxName + newEmpty.location = (centers[0], centers[1], centers[2]) + newEmpty.empty_draw_type = 'CUBE' + # newEmpty.empty_draw_size = (sizes[0], sizes[1], sizes[2]) + # newEmpty.dimensions = (sizes[0], sizes[1], sizes[2]) + # newEmpty.scale = (sizes[0]/2.0, sizes[1]/2.0, sizes[2]/2.0) + newEmpty.scale = (sizes[0], sizes[1], sizes[2]) + if enable_minetest: - xMin /= 10.0 - xMax /= 10.0 - yMin /= 10.0 - yMax /= 10.0 - zMin /= 10.0 - zMax /= 10.0 + for i in range(3): + mins[i] /= 10.0 + maxes[i] /= 10.0 msg = ('Size is not available. Make sure you have a mesh object' ' selected.') - if xMin is not None: + if mins[0] is not None: + # swap y and z for Minetest (y-up): if y_up: - tmp = yMin - yMin = zMin - zMin = tmp - tmp = yMax - yMax = zMax - zMax = tmp + tmp = mins[1] + mins[1] = mins[2] + mins[2] = tmp + tmp = maxes[1] + maxes[1] = maxes[2] + maxes[2] = tmp msg = (" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}," - " {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, - zMax)) + " {:.2f}, {:.2f}}}".format(mins[0], mins[1], mins[2], + maxes[0], maxes[1], maxes[2])) if len(msgSuffix) > 0: msgSuffix = " -- " + msgSuffix bpy.context.window_manager.clipboard = msg + msgSuffix