Browse Source

collisionbox: add Minetest h symmetry, enable by default

master
poikilos 5 years ago
committed by Jacob Gustafson
parent
commit
8e3ab3f19d
  1. 156
      utilities/blender/generate_lua_collisionbox.py

156
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") " object, Run Script")
y_up = True 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 import bpy
# from mathutils import Matrix # from mathutils import Matrix
@ -54,7 +65,7 @@ if ob1 is not None:
if ob1 is None: if ob1 is None:
msg = "Nothing is selected." msg = "Nothing is selected."
bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) 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." msg = "Collision box for armatures cannot be calculated."
bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg)
else: else:
@ -64,26 +75,14 @@ else:
# See https://blender.stackexchange.com/questions/8459/get-blender-x-y-z-and-bounding-box-with-script # 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] # 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 # 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("mesh:" + str(mesh))
# print("hasattr(mesh, 'vertices'):" # print("hasattr(mesh, 'vertices'):"
# + str(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: if mesh is not None:
xMin = None
yMin = None
zMin = None
xMax = None
yMax = None
zMax = None
wm = ob1.matrix_world wm = ob1.matrix_world
for vert in mesh.vertices: for vert in mesh.vertices:
# This matrix multiplication is NOT transitive. # This matrix multiplication is NOT transitive.
@ -91,24 +90,18 @@ else:
loc = wm @ vert.co loc = wm @ vert.co
except TypeError: except TypeError:
loc = wm * vert.co # Blender <2.8 loc = wm * vert.co # Blender <2.8
# switch y and z for Minetest (y-up) # NOTE: swap y and z for Minetest (y-up) LATER
if (xMin is None) or (loc.x < xMin): coords = (loc.x, loc.y, loc.z)
xMin = loc.x for i in range(3):
if (xMax is None) or (loc.x > xMax): if (mins[i] is None) or (coords[i] < mins[i]):
xMax = loc.x mins[i] = coords[i]
if (yMin is None) or (loc.y < yMin): if (maxes[i] is None) or (coords[i] > maxes[i]):
yMin = loc.y maxes[i] = coords[i]
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
# print(str(extents1)) # print(str(extents1))
# print("--by vertices (raw):") # print("--by vertices (raw):")
print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}," # print(" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f},"
" {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, # " {:.2f}, {:.2f}}}".format(mins[0], mins[2], mins[1],
zMax)) # maxes[0], maxes[2], maxes[1]))
# Use ob1.matrix_world (above) instead of incrementing # Use ob1.matrix_world (above) instead of incrementing
# ob1.location.x, y, and z # ob1.location.x, y, and z
@ -124,14 +117,14 @@ else:
# except TypeError: # except TypeError:
# loc = wm * vert.co # Blender <2.8 # loc = wm * vert.co # Blender <2.8
# isFar = False # 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 # 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 # isFar = True
# if isFar: # if isFar:
# pass # pass
# # result = bpy.ops.object.add(type='EMPTY', radius=.25, # # result = bpy.ops.object.add(type='EMPTY', radius=.25,
# # location=loc); # # location=loc)
# # NOTE: result is merely {'FINISHED'} # # NOTE: result is merely {'FINISHED'}
# # print("{:.2f}, {:.2f}, {:.2f}".format(loc.x, loc.y, # # print("{:.2f}, {:.2f}, {:.2f}".format(loc.x, loc.y,
# # loc.z)) # # loc.z))
@ -142,40 +135,83 @@ else:
else: else:
extents1 = ob1.scale.copy() extents1 = ob1.scale.copy()
# Object is an empty, so scale up for Minetest # Object is an empty, so scale up for Minetest
extents1.x = extents1.x * 2.0 extents1.x = extents1.x * (ob1.empty_draw_size * 2.0)
extents1.y = extents1.y * 2.0 extents1.y = extents1.y * (ob1.empty_draw_size * 2.0)
extents1.z = extents1.z * 2.0 extents1.z = extents1.z * (ob1.empty_draw_size * 2.0)
xMin = obj1Loc.x - extents1.x / 2.0 mins[0] = obj1Loc.x - extents1.x / 2.0
xMax = obj1Loc.x + extents1.x / 2.0 maxes[0] = obj1Loc.x + extents1.x / 2.0
yMin = obj1Loc.y - extents1.y / 2.0 mins[1] = obj1Loc.y - extents1.y / 2.0
yMax = obj1Loc.y + extents1.y / 2.0 maxes[1] = obj1Loc.y + extents1.y / 2.0
zMin = obj1Loc.z - extents1.z / 2.0 mins[2] = obj1Loc.z - extents1.z / 2.0
zMax = obj1Loc.z + extents1.z / 2.0 maxes[2] = obj1Loc.z + extents1.z / 2.0
msgSuffix = " (using Empty object's scale)" msgSuffix = " (using Empty object's scale)"
# print("--using empty object:") # 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: if enable_minetest:
xMin /= 10.0 for i in range(3):
xMax /= 10.0 mins[i] /= 10.0
yMin /= 10.0 maxes[i] /= 10.0
yMax /= 10.0
zMin /= 10.0
zMax /= 10.0
msg = ('Size is not available. Make sure you have a mesh object' msg = ('Size is not available. Make sure you have a mesh object'
' selected.') ' selected.')
if xMin is not None: if mins[0] is not None:
# swap y and z for Minetest (y-up):
if y_up: if y_up:
tmp = yMin tmp = mins[1]
yMin = zMin mins[1] = mins[2]
zMin = tmp mins[2] = tmp
tmp = yMax tmp = maxes[1]
yMax = zMax maxes[1] = maxes[2]
zMax = tmp maxes[2] = tmp
msg = (" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}," msg = (" collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f},"
" {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, " {:.2f}, {:.2f}}}".format(mins[0], mins[1], mins[2],
zMax)) maxes[0], maxes[1], maxes[2]))
if len(msgSuffix) > 0: if len(msgSuffix) > 0:
msgSuffix = " -- " + msgSuffix msgSuffix = " -- " + msgSuffix
bpy.context.window_manager.clipboard = msg + msgSuffix bpy.context.window_manager.clipboard = msg + msgSuffix

Loading…
Cancel
Save