diff --git a/Bucket_Game-branches/duck--add-190814/mods/codermobs/codermobs/src/duck3-feathers-collapsed.blend b/Bucket_Game-branches/duck--add-190814/mods/codermobs/codermobs/src/duck3-feathers-collapsed.blend index 69137f2..6ee2398 100644 Binary files a/Bucket_Game-branches/duck--add-190814/mods/codermobs/codermobs/src/duck3-feathers-collapsed.blend and b/Bucket_Game-branches/duck--add-190814/mods/codermobs/codermobs/src/duck3-feathers-collapsed.blend differ diff --git a/utilities/blender/generate_Empty_at_each_vertex.py b/utilities/blender/generate_Empty_at_each_vertex.py new file mode 100644 index 0000000..ea94f10 --- /dev/null +++ b/utilities/blender/generate_Empty_at_each_vertex.py @@ -0,0 +1,91 @@ +print("How to use: paste into a Blender Text Editor panel, select object, Run Script") + +y_up = True +enable_minetest = True + +import bpy +#from mathutils import Matrix +from mathutils import Vector +#from mathutils import Euler + +class MessageBox(bpy.types.Operator): + bl_idname = "message.messagebox" + bl_label = "" + message = bpy.props.StringProperty( + name = "message", + description = "message", + default = '' + ) + + def execute(self, context): + self.report({'INFO'}, self.message) + print(self.message) + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width = 400) + + def draw(self, context): + self.layout.label(self.message) + self.layout.label("") + #col = self.layout.column(align = True) + #col.prop(context.scene, "my_string_prop") + + +ob1 = None +try: + ob1 = obj.select_get() +except: + # < 2.8 + ob1 = bpy.context.scene.objects.active + +bpy.utils.register_class(MessageBox) + +if ob1 is None: + msg = "Nothing is selected." + # WRONG: https://stackoverflow.com/questions/7697532/how-to-show-a-message-from-a-blender-script + # self.report({'ERROR'}, msg) + bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) +else: + + loc1 = ob1.location + #loc1 = ob1.matrix_world.translation + + #see https://blender.stackexchange.com/questions/6139/how-to-iterate-through-all-vertices-of-an-object-that-contains-multiple-meshes + mesh = ob1.data + # print("mesh:" + str(mesh)) + # print("hasattr(mesh, 'vertices'):" + str(hasattr(mesh, 'vertices')))] + xMin = None + if (mesh is not None) and (not hasattr(mesh, 'vertices')): + print("--can't calculate collisionbox for skeleton") + elif mesh is not None: + xMin = None + yMin = None + zMin = None + xMax = None + yMax = None + zMax = None + # See https://blender.stackexchange.com/questions/6155/how-to-convert-coordinates-from-vertex-to-world-space + wm = ob1.matrix_world + newNamePrefix = "Empty.from." + ob1.name + i = 0 + for vert in mesh.vertices: + newName = newNamePrefix + "." + str(i) + # This matrix multiplication is NOT transitive. + try: + loc = wm @ vert.co + except TypeError: + loc = wm * vert.co # Blender <2.8 + bpy.ops.object.add(type = 'EMPTY', radius = .25, location = loc); + bpy.context.active_object.name = newName + # NOTE: add only returns {'FINISHED'} + # See also vert.co.x (and y and z) + + # add_named doesn't work this way--how is unknown: + # bpy.ops.object.add_named(name = "Empty" + ob1.name, type = 'EMPTY', radius = .25, location = loc) + i += 1 + +# bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) + +# Unregistering before user clicks the MessageBox will crash Blender! +# bpy.utils.unregister_class(MessageBox) diff --git a/utilities/blender/generate_lua_collisionbox.py b/utilities/blender/generate_lua_collisionbox.py new file mode 100644 index 0000000..44863b5 --- /dev/null +++ b/utilities/blender/generate_lua_collisionbox.py @@ -0,0 +1,192 @@ +print("How to use: paste into a Blender Text Editor panel, select object, Run Script") + +y_up = True +enable_minetest = False + + +import bpy +#from mathutils import Matrix +from mathutils import Vector +#from mathutils import Euler + +ob1 = None +try: + ob1 = obj.select_get() +except: + # < 2.8 + ob1 = bpy.context.scene.objects.active + + +class MessageBox(bpy.types.Operator): + bl_idname = "message.messagebox" + bl_label = "" + message = bpy.props.StringProperty( + name = "message", + description = "message", + default = '' + ) + + def execute(self, context): + self.report({'INFO'}, self.message) + print(self.message) + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width = 400) + + def draw(self, context): + self.layout.label(self.message) + self.layout.label("") + #col = self.layout.column(align = True) + #col.prop(context.scene, "my_string_prop") + + +bpy.utils.register_class(MessageBox) + +msgSuffix = "" + +mesh = None +if ob1 is not None: + mesh = ob1.data + +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')): + msg = "Collision box for armatures cannot be calculated." + bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) +else: + #print(str(thisO)) + #extents1 = ob1.dimensions.copy() + #if (extents1.x == 0.0) and (extents1.y == 0.0) and (extents1.z == 0.0) and (ob1.scale.x > 0.0): + #extents1.x = extents1.x / 10.0 + #extents1.y = extents1.y / 10.0 + #extents1.z = extents1.z / 10.0 + obj1Loc = ob1.location + #obj1Loc = ob1.matrix_world.translation + #print(str(extents1)) + + #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 + #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 + 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. + try: + 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 + # print(str(extents1)) + # print("--by vertices (raw):") + # print( + # " collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, zMax) + #) + + # use ob1.matrix_world instead of incrementing location. + # xMax += ob1.location.x + # xMin += ob1.location.x + # yMax += ob1.location.y + # yMin += ob1.location.y + # zMax += ob1.location.z + # zMin += ob1.location.z + + + # print("--by vertices:") + newNamePrefix = "Empty.EDGE." + ob1.name + i = 0 + wm = ob1.matrix_world + for vert in mesh.vertices: + newName = newNamePrefix + "." + str(i) + try: + loc = mat @ vert.co # NOT transitive + except TypeError: + loc = mat * vert.co # Blender <2.8 + isFar = True + if loc.x == xMax or loc.y == yMax or loc.z == zMax: + isFar = True + elif loc.x == xMin or loc.y == yMin or loc.z == zMin: + isFar = True + if isFar: + # result = bpy.ops.object.add(type = 'EMPTY', radius = .25, location = loc); + # NOTE: result is merely {'FINISHED'} + # print("{:.2f}, {:.2f}, {:.2f}".format(loc.x, loc.y, loc.z)) + + # bpy.ops.object.add_named(name = newName, type = 'EMPTY', radius = .25, location = loc) + i += 1 + 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 + msgSuffix = " (using Empty object's scale)" + # print("--using empty object:") + #switch y and z for Minetest (y-up): + if enable_minetest: + xMin /= 10.0 + xMax /= 10.0 + yMin /= 10.0 + yMax /= 10.0 + zMin /= 10.0 + zMax /= 10.0 + + msg = 'Size is not available. Make sure you have a mesh object selected.' + + if xMin is not None: + if y_up: + tmp = yMin + yMin = zMin + zMin = tmp + tmp = yMax + yMax = zMax + zMax = tmp + msg = " collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, zMax) + msg += msgSuffix + if enable_minetest: + msg += " #*10" + print(msg) + + bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) + +# Unregistering before user clicks the MessageBox will crash Blender! +# bpy.utils.unregister_class(MessageBox) diff --git a/utilities/generate_collision_box.py b/utilities/generate_collision_box.py deleted file mode 100644 index f97d314..0000000 --- a/utilities/generate_collision_box.py +++ /dev/null @@ -1,145 +0,0 @@ -print("How to use: paste into a Blender Text Editor panel, select object, Run Script") - -y_up = True -enable_minetest = True - -import bpy -#from mathutils import Matrix -from mathutils import Vector -#from mathutils import Euler - -ob1 = None -try: - ob1 = obj.select_get() -except: - # < 2.8 - ob1 = bpy.context.scene.objects.active - - -class MessageBox(bpy.types.Operator): - bl_idname = "message.messagebox" - bl_label = "" - message = bpy.props.StringProperty( - name = "message", - description = "message", - default = '' - ) - - def execute(self, context): - self.report({'INFO'}, self.message) - print(self.message) - return {'FINISHED'} - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width = 400) - - def draw(self, context): - self.layout.label(self.message) - self.layout.label("") - #col = self.layout.column(align = True) - #col.prop(context.scene, "my_string_prop") - - -bpy.utils.register_class(MessageBox) - -#print(str(thisO)) -#extents1 = ob1.dimensions.copy() -#if (extents1.x == 0.0) and (extents1.y == 0.0) and (extents1.z == 0.0) and (ob1.scale.x > 0.0): -#extents1.x = extents1.x / 10.0 -#extents1.y = extents1.y / 10.0 -#extents1.z = extents1.z / 10.0 -loc1 = ob1.location -#loc1 = ob1.matrix_world.translation -#print(str(extents1)) - -#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 -#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 -mesh = ob1.data -#print("mesh:" + str(mesh)) -#print("hasattr(mesh, 'vertices'):" + str(hasattr(mesh, 'vertices')))] -xMin = None -if (mesh is not None) and (not hasattr(mesh, 'vertices')): - print("--can't calculate collisionbox for skeleton") -elif mesh is not None: - xMin = None - yMin = None - zMin = None - xMax = None - yMax = None - zMax = None - for vert in mesh.vertices: - # switch y and z for Minetest (y-up) - if (xMin is None) or (vert.co.x < xMin): - xMin = vert.co.x - if (xMax is None) or (vert.co.x > xMax): - xMax = vert.co.x - if (yMin is None) or (vert.co.y < yMin): - yMin = vert.co.y - if (yMax is None) or (vert.co.y > yMax): - yMax = vert.co.y - if (zMin is None) or (vert.co.z < zMin): - zMin = vert.co.z - if (zMax is None) or (vert.co.z > zMax): - zMax = vert.co.z - #print(str(extents1)) - #print("--by vertices (raw):") - #print( - # " collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, zMax) - #) - xMax += ob1.location.x - xMin += ob1.location.x - yMax += ob1.location.y - yMin += ob1.location.y - zMax += ob1.location.z - zMin += ob1.location.z - print("--by vertices:") -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 = loc1.x - extents1.x / 2.0 - xMax = loc1.x + extents1.x / 2.0 - yMin = loc1.y - extents1.y / 2.0 - yMax = loc1.y + extents1.y / 2.0 - zMin = loc1.z - extents1.z / 2.0 - zMax = loc1.z + extents1.z / 2.0 - print("--using empty object:") -#switch y and z for Minetest (y-up): -if enable_minetest: - xMin /= 10.0 - xMax /= 10.0 - yMin /= 10.0 - yMax /= 10.0 - zMin /= 10.0 - zMax /= 10.0 - -msg = 'Size is not available. Make sure you have a mesh object selected.' - -if xMin is not None: - if y_up: - tmp = yMin - yMin = zMin - zMin = tmp - tmp = yMax - yMax = zMax - zMax = tmp - msg = " collisionbox = {{{:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}}}".format(xMin, yMin, zMin, xMax, yMax, zMax) - # print(msg) - -bpy.ops.message.messagebox('INVOKE_DEFAULT', message = msg) - -bpy.utils.unregister_class(MessageBox)