From c70aca0d2a6555b2426fa16eebc077ad2f5cc911 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 16 Jun 2018 22:54:37 -0700 Subject: [PATCH 1/4] cleaned up mesh exporter --- blender_addons/io_mesh_terasology/__init__.py | 223 ++++---- .../io_mesh_terasology/export_block_shape.py | 486 +++++++----------- 2 files changed, 278 insertions(+), 431 deletions(-) diff --git a/blender_addons/io_mesh_terasology/__init__.py b/blender_addons/io_mesh_terasology/__init__.py index 2693b4b..e598c59 100644 --- a/blender_addons/io_mesh_terasology/__init__.py +++ b/blender_addons/io_mesh_terasology/__init__.py @@ -1,151 +1,124 @@ #!BPY bl_info = { - "name": "Terasology Block Shape Export", - "description": "Exporter for producing Terasology Block Shape files (in JSON format)", - "author": "Immortius", - "version": (1, 3), - "blender": (2, 6, 3), - "location": "File > Import-Export", - "category": "Import-Export"} + "name": "Terasology Block Shape Export", + "description": "Exporter for producing Terasology Block Shape files (in JSON format)", + "author": "Immortius", + "version": (1, 3), + "blender": (2, 6, 3), + "location": "File > Import-Export", + "category": "Import-Export"} import bpy -import os -import bpy_extras.io_utils from bpy.props import StringProperty, BoolProperty +from . import export_block_shape -class ExportBlockShape(bpy.types.Operator, bpy_extras.io_utils.ExportHelper): - bl_idname = "export_mesh.terasology_block_shape" - bl_label = "Export Terasology Block Shape" - - filename_ext = ".shape" - filter_glob = StringProperty(default="*.shape", options={'HIDDEN'}) - - apply_modifiers = BoolProperty( - name="Apply Modifiers", - description="Apply Modifiers to the exported mesh", - default=True) - - @classmethod - def poll(cls, context): - return context.active_object != None - - def execute(self, context): - filepath = self.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - from . import export_block_shape - keywords = self.as_keywords(ignore=("filter_glob","check_existing")) - return export_block_shape.save(self, context, **keywords) - - def draw(self, context): - layout = self.layout - - row = layout.row() - row.prop(self, "apply_modifiers") - -#UI Panel +# UI Panel bpy.types.Scene.teraAuthor = StringProperty( - name="Author", - description="Is this side of the block complete", - default = "") + name="Author", + description="Is this side of the block complete", + default="") bpy.types.Scene.teraDisplayName = StringProperty( - name="Display Name", - description="The name of the shape, displayed in game", - default = "") - -bpy.types.Scene.teraCollisionType = bpy.props.EnumProperty( - name = "Collision Type", - description="Type of collision to use for this block", - items = [("FullCube", "Full Cube", "The entire block is solid"), - ("AutoAABB", "Auto AABB", "An AABB is calculated that encompasses the block mesh"), - ("ConvexHull", "Auto Convex Hull", "A convex hull is calculated that encompasses the block mesh"), - ("Manual", "Manual", "One or more colliders are specified to describe the collision")]) - + name="Display Name", + description="The name of the shape, displayed in game", + default="") + +bpy.types.Scene.teraCollisionType = bpy.props.EnumProperty( + name="Collision Type", + description="Type of collision to use for this block", + items=[("FullCube", "Full Cube", "The entire block is solid"), + ("AutoAABB", "Auto AABB", "An AABB is calculated that encompasses the block mesh"), + ("ConvexHull", "Auto Convex Hull", "A convex hull is calculated that encompasses the block mesh"), + ("Manual", "Manual", "One or more colliders are specified to describe the collision")]) + bpy.types.Scene.teraCollisionSymmetric = BoolProperty( - name="Is Collision Symmetric", - description="Whether the collision is symmetric for all rotations of the block", - default = False) - + name="Is Collision Symmetric", + description="Whether the collision is symmetric for all rotations of the block", + default=False) + bpy.types.Scene.teraCollisionSymmetricX = BoolProperty( - name="Is Symmetric Around X", - description="Whether the block is symmetric when rotating around X (in Blender)", - default = False) + name="Is Symmetric Around X", + description="Whether the block is symmetric when rotating around X (in Blender)", + default=False) bpy.types.Scene.teraCollisionSymmetricY = BoolProperty( - name="Is Symmetric Around Y", - description="Whether the block is symmetric when rotating around Y (in Blender)", - default = False) - + name="Is Symmetric Around Y", + description="Whether the block is symmetric when rotating around Y (in Blender)", + default=False) + bpy.types.Scene.teraCollisionSymmetricZ = BoolProperty( - name="Is Symmetric Around Z", - description="Whether the block is symmetric when rotating around Z (in Blender)", - default = False) - + name="Is Symmetric Around Z", + description="Whether the block is symmetric when rotating around Z (in Blender)", + default=False) + bpy.types.Scene.teraBillboardNormals = BoolProperty( - name="Use Billboard Normals", - description="Are normals set up for billboards (pointing up)", - default = False) - + name="Use Billboard Normals", + description="Are normals set up for billboards (pointing up)", + default=False) + bpy.types.Object.teraFullSide = BoolProperty( - name="Full Side", - description="Is this side of the block complete", - default = False) - -bpy.types.Object.teraColliderType = bpy.props.EnumProperty( - name = "Collider Type", - description="Type of collider this mesh provides", - items = [("None", "None", "This mesh is not a collider"), - ("AABB", "AABB", "This mesh provides a aabb collider"), - ("Sphere", "Sphere", "This mesh provides a sphere collider")], - default = "None") - + name="Full Side", + description="Is this side of the block complete", + default=False) + +bpy.types.Object.teraColliderType = bpy.props.EnumProperty( + name="Collider Type", + description="Type of collider this mesh provides", + items=[("None", "None", "This mesh is not a collider"), + ("AABB", "AABB", "This mesh provides a aabb collider"), + ("Sphere", "Sphere", "This mesh provides a sphere collider")], + default="None") + + class TeraScenePropUIPanel(bpy.types.Panel): - bl_label = "Terasology Scene Properties" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - - def draw(self, context): - layout = self.layout - scene = context.scene - if not scene: - return - layout.prop(scene, 'teraAuthor') - layout.prop(scene, 'teraCollisionType') - layout.prop(scene, 'teraCollisionSymmetric') - layout.prop(scene, 'teraCollisionSymmetricX') - layout.prop(scene, 'teraCollisionSymmetricY') - layout.prop(scene, 'teraCollisionSymmetricZ') - layout.prop(scene, 'teraBillboardNormals') - + bl_label = "Terasology Scene Properties" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + + def draw(self, context): + layout = self.layout + scene = context.scene + if not scene: + return + layout.prop(scene, 'teraAuthor') + layout.prop(scene, 'teraCollisionType') + layout.prop(scene, 'teraCollisionSymmetric') + layout.prop(scene, 'teraCollisionSymmetricX') + layout.prop(scene, 'teraCollisionSymmetricY') + layout.prop(scene, 'teraCollisionSymmetricZ') + layout.prop(scene, 'teraBillboardNormals') + + class TeraObjectPropUIPanel(bpy.types.Panel): - bl_label = "Terasology Mesh Properties" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - - def draw(self, context): - layout = self.layout - - ob = context.object - if not ob: - return - if not ob.type == 'MESH': - return - - layout.prop(ob, 'teraFullSide') - layout.prop(ob, 'teraColliderType') - + bl_label = "Terasology Mesh Properties" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + + def draw(self, context): + layout = self.layout + + ob = context.object + if not ob: + return + if not ob.type == 'MESH': + return + + layout.prop(ob, 'teraFullSide') + layout.prop(ob, 'teraColliderType') + + def menu_export(self, context): - self.layout.operator(ExportBlockShape.bl_idname, text="Terasology Block Shape (.shape)") + self.layout.operator(export_block_shape.ExportToBlockShape.bl_idname, text="Terasology Block Shape (.shape)") + def register(): - bpy.utils.register_module(__name__) - bpy.types.INFO_MT_file_export.append(menu_export) + bpy.utils.register_module(__name__) + bpy.types.INFO_MT_file_export.append(menu_export) + def unregister(): - bpy.utils.unregister_module(__name__) - bpy.types.INFO_MT_file_export.remove(menu_export) + bpy.utils.unregister_module(__name__) + bpy.types.INFO_MT_file_export.remove(menu_export) -if __name__ == "__main__": - register() \ No newline at end of file diff --git a/blender_addons/io_mesh_terasology/export_block_shape.py b/blender_addons/io_mesh_terasology/export_block_shape.py index 9bfabf4..e45c33a 100644 --- a/blender_addons/io_mesh_terasology/export_block_shape.py +++ b/blender_addons/io_mesh_terasology/export_block_shape.py @@ -1,320 +1,194 @@ -""" -This script exports Terasology block shapes from Blender. These are exported as as .groovy files. -Each block should be centered on the origin, and contain sub meshes with the following names: - - Center - - Top - - Bottom - - Front - - Back - - Left - - Right -Each side can be given a custom property of "teraFullSide" to denote that it fills that direction. -There are also properties to handle collision -""" - import bpy -import os +from bpy.props import FloatProperty, IntProperty, BoolProperty, StringProperty, CollectionProperty, FloatVectorProperty, \ + EnumProperty, IntVectorProperty +import bpy_extras.io_utils +import json import datetime -import math -import mathutils from mathutils import Vector -def convertVec3d(v): - return -v[0], v[2], v[1] - -def convertVec3dAbs(v): - return v[0], v[2], v[1] -def writeAABBCollision( - fw, - scene): - parts = ["Center", "Top", "Bottom", "Front", "Back", "Left", "Right"] +class ExportToBlockShape(bpy.types.Operator, bpy_extras.io_utils.ExportHelper): + bl_idname = "object.terasology_block_shape" + bl_label = "Export Terasology Block Shape" + + filename_ext = ".shape" + filter_glob = StringProperty(default="*.shape", options={'HIDDEN'}) + + apply_modifiers = BoolProperty( + name="Apply Modifiers", + description="Apply Modifiers to the exported mesh", + default=True) + + @classmethod + def poll(cls, context): + return context.active_object != None + + def meshify(self, obj, scene, apply_modifiers): + if not obj: + return + + mesh = None + if apply_modifiers: + mesh = obj.to_mesh(scene, True, 'PREVIEW') + else: + mesh = obj.data + mesh.update(calc_tessface=True) + + result = {} + result['vertices'] = [] + result['normals'] = [] + result['texcoords'] = [] + result['faces'] = [] + + for v in mesh.vertices: + result['vertices'].append([-v.co[0], v.co[2], v.co[1]]) + + for i, face in enumerate(mesh.tessfaces): + for j, index in enumerate(face.vertices): + vert = mesh.vertices[index] + if scene.teraBillboardNormals: + normal = [0, 0, 1] + elif face.use_smooth: + normal = tuple(face.normal) + else: + normal = tuple(vert.normal) + uvtemp = mesh.tessface_uv_textures.active.data[i].uv[j] + uvs = uvtemp[0], 1.0 - uvtemp[1] + result['normals'].append([-normal[0], normal[2], normal[1]]) + result['texcoords'].append(uvs) + result['faces'].append([f for f in face.vertices]) + + if apply_modifiers: + bpy.data.meshes.remove(mesh) + + return result + + def AABBCollider(self, obj): + if not obj: + return + mesh = obj.data + min = [100000.0, 100000.0, 100000.0] + max = [-100000.0, -100000.0, -100000.0] + + for faceNum, f in enumerate(mesh.faces): + faceVerts = f.vertices + for vertNum, index in enumerate(faceVerts): + vert = mesh.vertices[index].co + for i in range(3): + if vert[i] > max[i]: + max[i] = vert[i] + elif vert[i] < min[i]: + min[i] = vert[i] + + pos = [0.0, 0.0, 0.0] + dim = [0.0, 0.0, 0.0] + + for i in range(3): + pos[i] = 0.5 * (max[i] + min[i]) + dim[i] = 0.5 * (max[i] - min[i]) + + return { + 'type': 'AABB', + 'position': [-pos[0], pos[2], pos[1]], + 'extents': [-dim[0], dim[2], dim[1]] + } + + def sphereCollider(self, obj): + mesh = obj.data + center = Vector((0, 0, 0)) + + for v in mesh.vertices: + center += v.co + center /= len(mesh.vertices) + radius = 0.0 + for v in mesh.vertices: + dist = (center - v.co).length + radius = max(dist, radius) + return { + 'type': 'Sphere', + 'position': [-center[0], center[2], center[1]], + 'radius': radius + } + + def execute(self, context): + path = bpy.path.ensure_ext(self.filepath, self.filename_ext) - min = [100000.0,100000.0,100000.0] - max = [-100000.0,-100000.0,-100000.0] - - for part in parts: - if part in bpy.data.objects: - mesh = bpy.data.objects[part].data - for faceNum, f in enumerate(mesh.faces): - faceVerts = f.vertices - for vertNum, index in enumerate(faceVerts): - vert = mesh.vertices[index].co - for i in range(3): - if vert[i] > max[i]: - max[i] = vert[i] - elif vert[i] < min[i]: - min[i] = vert[i] - - pos = [0.0,0.0,0.0] - dim = [0.0,0.0,0.0] - - for i in range(3): - pos[i] = 0.5 * (max[i] + min[i]) - dim[i] = 0.5 * (max[i] - min[i]) - fw(',\n "collision" : {\n') - if scene.teraCollisionSymmetric: - fw(' "symmetric" : true,\n') - if scene.teraCollisionSymmetricZ: - fw(' "yawSymmetric" : true,\n') - if scene.teraCollisionSymmetricX: - fw(' "pitchSymmetric" : true,\n') - if scene.teraCollisionSymmetricY: - fw(' "rollSymmetric" : true,\n') - fw(' "colliders" : [\n') - fw(' {\n') - fw(' "type" : "AABB",\n') - fw(' "position" : [%.6f, %.6f, %.6f],\n' % convertVec3d(pos)) - fw(' "extents" : [%.6f, %.6f, %.6f]\n' % convertVec3dAbs(dim)) - fw(' }\n') - fw(' ]\n') - fw(' }') - -def writeConvexHullCollision( - fw, - scene): - fw(',\n "collision" : {\n') - if scene.teraCollisionSymmetric: - fw(' "symmetric" : true,\n') - if scene.teraCollisionSymmetricZ: - fw(' "yawSymmetric" : true,\n') - if scene.teraCollisionSymmetricX: - fw(' "pitchSymmetric" : true,\n') - if scene.teraCollisionSymmetricY: - fw(' "rollSymmetric" : true,\n') - fw(' "convexHull" : true\n') - fw(' }') - -def writeMeshCollision( - fw, - scene): - fw(',\n "collision" : {\n') - if scene.teraCollisionSymmetric: - fw(' "symmetric" : true,\n') - if scene.teraCollisionSymmetricZ: - fw(' "yawSymmetric" : true,\n') - if scene.teraCollisionSymmetricX: - fw(' "pitchSymmetric" : true,\n') - if scene.teraCollisionSymmetricY: - fw(' "rollSymmetric" : true,\n') - first = True - for object in bpy.data.objects: - if object.teraColliderType != '' and object.teraColliderType != 'None': - if first: - fw(' "colliders" : [\n') - first = False - else: - fw(",\n") - if object.teraColliderType == 'AABB': - writeAABBCollider(object, fw, scene) - elif object.teraColliderType == 'Sphere': - writeSphereCollider(object, fw, scene) - if not first: - fw("\n ]\n") - fw(' }') - -def writeSphereCollider( - obj, - fw, - scene): - if not obj: - return - - mesh = obj.data - - center = Vector((0, 0, 0)) - - for v in mesh.vertices: - center += v.co - - center /= len(mesh.vertices) - - radius = 0.0 - for v in mesh.vertices: - dist = (center - v.co).length - radius = max(dist, radius) - - - fw(" {\n") - fw(' "type" : "Sphere",\n') - fw(' "position" : [%.6f, %.6f, %.6f],\n' % convertVec3d(center)) - fw(' "radius" : %.6f\n' % radius) - fw(" }") - -def writeAABBCollider( - obj, - fw, - scene): - if not obj: - return - - mesh = obj.data - - min = [100000.0,100000.0,100000.0] - max = [-100000.0,-100000.0,-100000.0] - - for faceNum, f in enumerate(mesh.faces): - faceVerts = f.vertices - for vertNum, index in enumerate(faceVerts): - vert = mesh.vertices[index].co - for i in range(3): - if vert[i] > max[i]: - max[i] = vert[i] - elif vert[i] < min[i]: - min[i] = vert[i] - - pos = [0.0,0.0,0.0] - dim = [0.0,0.0,0.0] - - for i in range(3): - pos[i] = 0.5 * (max[i] + min[i]) - dim[i] = 0.5 * (max[i] - min[i]) - - fw(" {\n") - fw(' "type" : "AABB",\n') - fw(' "position" : [%.6f, %.6f, %.6f],\n' % convertVec3d(pos)) - fw(' "extents" : [%.6f, %.6f, %.6f]\n' % convertVec3dAbs(dim)) - fw(" }") + result = {} + result['displayName'] = context.scene.teraDisplayName + result['author'] = context.scene.teraAuthor -def writeMeshPart(name, - obj, - fw, - scene, - apply_modifiers - ): - - def roundVec3d(v): - return round(v[0], 6), round(v[1], 6), round(v[2], 6) - + now = datetime.datetime.now() + result['exportDate'] = '{:%Y-%m-%d %H:%M:%S}'.format(now) - def roundVec2d(v): - return round(v[0], 6), round(v[1], 6) - - if not obj: - return - - if apply_modifiers: - mesh = obj.to_mesh(scene, True, 'PREVIEW') - else: - mesh = obj.data - - - mesh.update(calc_tessface=True) + bpy.ops.object.mode_set(mode='OBJECT') + parts = ["Center", "Top", "Bottom", "Front", "Back", "Left", "Right"] + for part in parts: + if part in bpy.data.objects: + result[part.lower()] = self.meshify(bpy.data.objects[part], context.scene, self.apply_modifiers) + if ("teraFullSide" in bpy.data.objects[part]): + result[part.lower()]['fullSide'] = bpy.data.objects[part].teraFullSide + else: + result[part.lower()]['fullSide'] = False - processedVerts = [] - processedFaces = [[] for f in range(len(mesh.tessfaces))] - - for i, f in enumerate(mesh.tessfaces): - faceVerts = f.vertices - for j, index in enumerate(faceVerts): - vert = mesh.vertices[index] - if scene.teraBillboardNormals: - normal = [0,0,1] - elif f.use_smooth: - normal = tuple(f.normal) - else: - normal = tuple(vert.normal) - uvtemp = mesh.tessface_uv_textures.active.data[i].uv[j] - uvs = uvtemp[0], 1.0 - uvtemp[1] - - processedFaces[i].append(len(processedVerts)) - processedVerts.append((vert, normal, uvs)) - - - fw(',\n "%s" : {\n' % name.lower()) - - fw(' "vertices" : [') - first = True - for i, v in enumerate(processedVerts): - if not first: - fw(", ") - fw("[%.6f, %.6f, %.6f]" % convertVec3d(v[0].co)) - first = False - fw("],\n") - - fw(' "normals" : [') - first = True - for i, v in enumerate(processedVerts): - if not first: - fw(", ") - fw("[%.6f, %.6f, %.6f]" % convertVec3d(v[1])) - first = False - fw("],\n") - - fw(' "texcoords" : [') - first = True - for i, v in enumerate(processedVerts): - if not first: - fw(", ") - fw("[%.6f, %.6f]" % v[2]) - first = False - fw("],\n") - - fw(' "faces" : [\n') - firstFace = True - for face in processedFaces: - if not firstFace: - fw(",\n") - fw(" [") - - first = True - for ind in face: - if not first: - fw(", ") - fw("%d" % ind) - first = False - fw("]") - firstFace = False - fw("\n ],\n") - if "teraFullSide" in obj: - if obj.teraFullSide: - fw(' "fullSide" : true\n'); - else: - fw(' "fullSide" : false\n'); - else: - fw(' "fullSide" : false\n'); - - fw(" }") - - if apply_modifiers: - bpy.data.meshes.remove(mesh) + hasColliders = False + result['collision'] = {} + if context.scene.teraCollisionType == "AutoAABB": + min = [100000.0, 100000.0, 100000.0] + max = [-100000.0, -100000.0, -100000.0] + for part in parts: + if part in bpy.data.objects: + mesh = bpy.data.objects[part].data + for faceNum, f in enumerate(mesh.faces): + faceVerts = f.vertices + for vertNum, index in enumerate(faceVerts): + vert = mesh.vertices[index].co + for i in range(3): + if vert[i] > max[i]: + max[i] = vert[i] + elif vert[i] < min[i]: + min[i] = vert[i] + pos = [0.0, 0.0, 0.0] + dim = [0.0, 0.0, 0.0] + for i in range(3): + pos[i] = 0.5 * (max[i] + min[i]) + dim[i] = 0.5 * (max[i] - min[i]) + hasColliders = True + result['collision']['colliders'] = [{ + 'type': 'AABB', + 'position': [-pos[0], pos[2], pos[1]], + 'extents': [-dim[0], dim[2], dim[1]] + }] + elif context.scene.teraCollisionType == "ConvexHull": + result['collision']['convexHull'] = True + hasColliders = True + elif context.scene.teraCollisionType == "Manual": + hasColliders = True + result['collision']['colliders'] = [] + for object in bpy.data.objects: + if object.teraColliderType != '' and object.teraColliderType == 'None': + if object.teraColliderType == 'AABB': + result['collision']['colliders'].append(self.AABBCollider(object)) + elif object.teraColliderType == 'Sphere': + result['collision']['colliders'].append(self.sphereCollider(object)) -def save(operator, - context, - filepath="", - apply_modifiers=True - ): + if (hasColliders == True): + result['collision']["symmetric"] = context.scene.teraCollisionSymmetric + result['collision']['yawSymmetric'] = context.scene.teraCollisionSymmetricZ + result['collision']['pitchSymmetric'] = context.scene.teraCollisionSymmetricX + result['collision']['rollSymmetric'] = context.scene.teraCollisionSymmetricY - scene = context.scene + file = open(path, "w", encoding="utf8") + print("saving complete: %r " % path) + file.write(json.dumps(result, indent=4, separators=(',', ': '))) + file.close() + # filepath = self.filepath - file = open(filepath, "w", encoding="utf8", newline="\n") - fw = file.write - fw("{\n") - - fw(' "displayName" : "%s",\n' % scene.teraDisplayName) - fw(' "author" : "%s",\n' % scene.teraAuthor) - - now = datetime.datetime.now() - - fw(' "exportDate" : "%s"' % '{:%Y-%m-%d %H:%M:%S}'.format(now)) - - bpy.ops.object.mode_set(mode='OBJECT') - - parts = ["Center", "Top", "Bottom", "Front", "Back", "Left", "Right"] - - for part in parts: - if part in bpy.data.objects: - writeMeshPart(part, bpy.data.objects[part], fw, scene, apply_modifiers); + # from . import _export_block_shape + # keywords = self.as_keywords(ignore=("filter_glob", "check_existing")) + return {'FINISHED'} - if scene.teraCollisionType == "AutoAABB": - writeAABBCollision(fw, scene) - elif scene.teraCollisionType == "ConvexHull": - writeConvexHullCollision(fw, scene) - elif scene.teraCollisionType == "Manual": - writeMeshCollision(fw, scene) - - fw("\n}\n") - file.close() - print("saving complete: %r " % filepath) + def draw(self, context): + layout = self.layout - return {'FINISHED'} + row = layout.row() + row.prop(self, "apply_modifiers") From bd88de135b1ce9c0e22e5a5f9b5cf9d98006497e Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 17 Jun 2018 22:10:58 -0700 Subject: [PATCH 2/4] updated block shapes --- blender_addons/io_mesh_terasology/__init__.py | 5 + .../io_mesh_terasology/constants.py | 1 + .../io_mesh_terasology/export_block_shape.py | 91 ++++++--------- .../io_mesh_terasology/import_block_shape.py | 108 ++++++++++++++++++ 4 files changed, 151 insertions(+), 54 deletions(-) create mode 100644 blender_addons/io_mesh_terasology/constants.py create mode 100644 blender_addons/io_mesh_terasology/import_block_shape.py diff --git a/blender_addons/io_mesh_terasology/__init__.py b/blender_addons/io_mesh_terasology/__init__.py index e598c59..303383d 100644 --- a/blender_addons/io_mesh_terasology/__init__.py +++ b/blender_addons/io_mesh_terasology/__init__.py @@ -12,6 +12,7 @@ import bpy from bpy.props import StringProperty, BoolProperty from . import export_block_shape +from . import import_block_shape # UI Panel @@ -112,13 +113,17 @@ def draw(self, context): def menu_export(self, context): self.layout.operator(export_block_shape.ExportToBlockShape.bl_idname, text="Terasology Block Shape (.shape)") +def menu_import(self, context): + self.layout.operator(import_block_shape.ImportToBlockShape.bl_idname,text="Terasology Block Shape (.shape)") def register(): bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_export.append(menu_export) + bpy.types.INFO_MT_file_import.append(menu_import) def unregister(): bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_export.remove(menu_export) + bpy.types.INFO_MT_file_import.remove(menu_import) diff --git a/blender_addons/io_mesh_terasology/constants.py b/blender_addons/io_mesh_terasology/constants.py new file mode 100644 index 0000000..746bdb3 --- /dev/null +++ b/blender_addons/io_mesh_terasology/constants.py @@ -0,0 +1 @@ +PARTS = ["Center", "Top", "Bottom", "Front", "Back", "Left", "Right"] \ No newline at end of file diff --git a/blender_addons/io_mesh_terasology/export_block_shape.py b/blender_addons/io_mesh_terasology/export_block_shape.py index e45c33a..a422580 100644 --- a/blender_addons/io_mesh_terasology/export_block_shape.py +++ b/blender_addons/io_mesh_terasology/export_block_shape.py @@ -5,10 +5,10 @@ import json import datetime from mathutils import Vector - +from . import constants class ExportToBlockShape(bpy.types.Operator, bpy_extras.io_utils.ExportHelper): - bl_idname = "object.terasology_block_shape" + bl_idname = "export_scene.shape" bl_label = "Export Terasology Block Shape" filename_ext = ".shape" @@ -40,8 +40,11 @@ def meshify(self, obj, scene, apply_modifiers): result['texcoords'] = [] result['faces'] = [] + temp_verts = [] for v in mesh.vertices: result['vertices'].append([-v.co[0], v.co[2], v.co[1]]) + result['normals'].append(None) + result['texcoords'].append(None) for i, face in enumerate(mesh.tessfaces): for j, index in enumerate(face.vertices): @@ -54,8 +57,8 @@ def meshify(self, obj, scene, apply_modifiers): normal = tuple(vert.normal) uvtemp = mesh.tessface_uv_textures.active.data[i].uv[j] uvs = uvtemp[0], 1.0 - uvtemp[1] - result['normals'].append([-normal[0], normal[2], normal[1]]) - result['texcoords'].append(uvs) + result['normals'][index] = [-normal[0], normal[2], normal[1]] + result['texcoords'][index] = uvs result['faces'].append([f for f in face.vertices]) if apply_modifiers: @@ -63,22 +66,23 @@ def meshify(self, obj, scene, apply_modifiers): return result - def AABBCollider(self, obj): - if not obj: - return - mesh = obj.data + def AABBCollider(self, objs): + min = [100000.0, 100000.0, 100000.0] max = [-100000.0, -100000.0, -100000.0] - for faceNum, f in enumerate(mesh.faces): - faceVerts = f.vertices - for vertNum, index in enumerate(faceVerts): - vert = mesh.vertices[index].co - for i in range(3): - if vert[i] > max[i]: - max[i] = vert[i] - elif vert[i] < min[i]: - min[i] = vert[i] + for obj in objs: + if not obj: + return + mesh = obj.data + for face in mesh.faces: + for index in enumerate(face.vertices): + vert = mesh.vertices[index].co + for i in range(3): + if vert[i] > max[i]: + max[i] = vert[i] + elif vert[i] < min[i]: + min[i] = vert[i] pos = [0.0, 0.0, 0.0] dim = [0.0, 0.0, 0.0] @@ -93,17 +97,20 @@ def AABBCollider(self, obj): 'extents': [-dim[0], dim[2], dim[1]] } - def sphereCollider(self, obj): - mesh = obj.data - center = Vector((0, 0, 0)) + def sphereCollider(self, objs): - for v in mesh.vertices: - center += v.co - center /= len(mesh.vertices) + center = Vector((0, 0, 0)) radius = 0.0 - for v in mesh.vertices: - dist = (center - v.co).length - radius = max(dist, radius) + for obj in objs: + if not obj: + return + mesh = obj.data + for v in mesh.vertices: + center += v.co + center /= len(mesh.vertices) + for v in mesh.vertices: + dist = (center - v.co).length + radius = max(dist, radius) return { 'type': 'Sphere', 'position': [-center[0], center[2], center[1]], @@ -121,8 +128,7 @@ def execute(self, context): result['exportDate'] = '{:%Y-%m-%d %H:%M:%S}'.format(now) bpy.ops.object.mode_set(mode='OBJECT') - parts = ["Center", "Top", "Bottom", "Front", "Back", "Left", "Right"] - for part in parts: + for part in constants.PARTS: if part in bpy.data.objects: result[part.lower()] = self.meshify(bpy.data.objects[part], context.scene, self.apply_modifiers) if ("teraFullSide" in bpy.data.objects[part]): @@ -133,31 +139,8 @@ def execute(self, context): hasColliders = False result['collision'] = {} if context.scene.teraCollisionType == "AutoAABB": - min = [100000.0, 100000.0, 100000.0] - max = [-100000.0, -100000.0, -100000.0] - for part in parts: - if part in bpy.data.objects: - mesh = bpy.data.objects[part].data - for faceNum, f in enumerate(mesh.faces): - faceVerts = f.vertices - for vertNum, index in enumerate(faceVerts): - vert = mesh.vertices[index].co - for i in range(3): - if vert[i] > max[i]: - max[i] = vert[i] - elif vert[i] < min[i]: - min[i] = vert[i] - pos = [0.0, 0.0, 0.0] - dim = [0.0, 0.0, 0.0] - for i in range(3): - pos[i] = 0.5 * (max[i] + min[i]) - dim[i] = 0.5 * (max[i] - min[i]) hasColliders = True - result['collision']['colliders'] = [{ - 'type': 'AABB', - 'position': [-pos[0], pos[2], pos[1]], - 'extents': [-dim[0], dim[2], dim[1]] - }] + result['collision']['colliders'] = [self.AABBCollider([o for o in bpy.data.objects if o.name in constants.PARTS])] elif context.scene.teraCollisionType == "ConvexHull": result['collision']['convexHull'] = True hasColliders = True @@ -167,9 +150,9 @@ def execute(self, context): for object in bpy.data.objects: if object.teraColliderType != '' and object.teraColliderType == 'None': if object.teraColliderType == 'AABB': - result['collision']['colliders'].append(self.AABBCollider(object)) + result['collision']['colliders'].append(self.AABBCollider([object])) elif object.teraColliderType == 'Sphere': - result['collision']['colliders'].append(self.sphereCollider(object)) + result['collision']['colliders'].append(self.sphereCollider([object])) if (hasColliders == True): result['collision']["symmetric"] = context.scene.teraCollisionSymmetric diff --git a/blender_addons/io_mesh_terasology/import_block_shape.py b/blender_addons/io_mesh_terasology/import_block_shape.py new file mode 100644 index 0000000..64e11a4 --- /dev/null +++ b/blender_addons/io_mesh_terasology/import_block_shape.py @@ -0,0 +1,108 @@ +import os + +import bpy +import bpy_extras.io_utils +import json +import datetime +from mathutils import Vector +from . import constants +import bmesh + +class ImportToBlockShape(bpy.types.Operator, bpy_extras.io_utils.ImportHelper): + bl_idname = "import_scene.shape" + bl_label = "Import Terasology Block Shape" + + filename_ext = ".shape" + + def create_AABB + + def execute(self, context): + path = bpy.path.ensure_ext(self.filepath, self.filename_ext) + + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + if bpy.ops.object.select_all.poll(): + bpy.ops.object.select_all(action='DESELECT') + + file = open(path) + payload = json.loads(file.read()) + + context.scene.teraDisplayName = payload['displayName'] + context.scene.teraAuthor = payload['author'] + + + for part in constants.PARTS: + if(part.lower() in payload): + sub_payload = payload[part.lower()] + + if 'fullSide' in sub_payload: + bpy.data.objects[part].teraFullSide = sub_payload['fullSide'] + else: + bpy.data.objects[part].teraFullSide = False + + bm = bmesh.new() + verticies = [] + for v in sub_payload['vertices']: + verticies.append(bm.verts.new((-v[0],v[1],v[2]))) + + bm.verts.ensure_lookup_table() + bm.verts.index_update() + + for face_index in sub_payload['faces']: + bm.faces.new([verticies[i] for i in face_index]) + + uv_layer = bm.loops.layers.uv.new() + for face in bm.faces: + for loop in face.loops: + uv = sub_payload['texcoords'][loop.vert.index] + loop[uv_layer].uv = (uv[0],1.0 - uv[1]) + + mesh = bpy.data.meshes.new("mesh") + bm.to_mesh(mesh) + object = bpy.data.objects.new(part.capitalize(),mesh) + bpy.context.scene.objects.link(object) + bm.free() + + payload_collision = None + if 'collision' in payload: + payload_collision = payload['collision'] + if(payload_collision != None): + if 'symmetric' in payload_collision: + context.scene.teraCollisionSymmetric = payload_collision['symmetric'] + if 'yawSymmetric' in payload_collision: + context.scene.teraCollisionSymmetricZ = payload_collision['yawSymmetric'] + if 'pitchSymmetric' in payload_collision: + context.scene.teraCollisionSymmetricX = payload_collision['pitchSymmetric'] + if 'rollSymmetric' in payload_collision: + context.scene.teraCollisionSymmetricY = payload_collision['rollSymmetric'] + if 'convexHull' in payload_collision: + context.scene.teraCollisionSymmetric = payload_collision['convexHull'] + context.scene.teraCollisionType = "ConvexHull" + else: + if 'colliders' in payload_collision: + for collider in payload_collision['colliders']: + if(collider['type'] == 'AABB'): + p = collider['position'] + e = collider['extents'] + obj = bpy.ops.mesh.primitive_cube_add(location=(-p[0], p[1], p[2])) + obj.scale = (-e[0],e[1],e[2]) + obj.select = True + bpy.context.scene.objects.active = obj + bpy.ops.object.transform_apply(location=True, scale=True, rotation=True) + obj.select = False + elif collider['type'] == 'Sphere': + p = collider['position'] + r = collider['radius'] + obj = bpy.ops.mesh.primitive_uv_sphere_add(segments=16,location = (-p[0], p[1], p[2]),size = r) + obj.select = True + bpy.context.scene.objects.active = obj + bpy.ops.object.transform_apply(location=True, scale=True, rotation=True) + obj.select = False + + + + + + + return {'FINISHED'} \ No newline at end of file From e24be2c76990e2b1548044359175edc27337668f Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 17 Jun 2018 22:23:47 -0700 Subject: [PATCH 3/4] fixed fullside --- .../io_mesh_terasology/import_block_shape.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/blender_addons/io_mesh_terasology/import_block_shape.py b/blender_addons/io_mesh_terasology/import_block_shape.py index 64e11a4..1df7b82 100644 --- a/blender_addons/io_mesh_terasology/import_block_shape.py +++ b/blender_addons/io_mesh_terasology/import_block_shape.py @@ -14,8 +14,6 @@ class ImportToBlockShape(bpy.types.Operator, bpy_extras.io_utils.ImportHelper): filename_ext = ".shape" - def create_AABB - def execute(self, context): path = bpy.path.ensure_ext(self.filepath, self.filename_ext) @@ -28,19 +26,16 @@ def execute(self, context): file = open(path) payload = json.loads(file.read()) - context.scene.teraDisplayName = payload['displayName'] - context.scene.teraAuthor = payload['author'] + if 'displayName' in payload: + context.scene.teraDisplayName = payload['displayName'] + if 'author' in payload: + context.scene.teraAuthor = payload['author'] for part in constants.PARTS: if(part.lower() in payload): sub_payload = payload[part.lower()] - if 'fullSide' in sub_payload: - bpy.data.objects[part].teraFullSide = sub_payload['fullSide'] - else: - bpy.data.objects[part].teraFullSide = False - bm = bmesh.new() verticies = [] for v in sub_payload['vertices']: @@ -64,6 +59,12 @@ def execute(self, context): bpy.context.scene.objects.link(object) bm.free() + if 'fullSide' in sub_payload: + bpy.data.objects[part.capitalize()].teraFullSide = sub_payload['fullSide'] + else: + bpy.data.objects[part.capitalize()].teraFullSide = False + + payload_collision = None if 'collision' in payload: payload_collision = payload['collision'] @@ -100,9 +101,4 @@ def execute(self, context): bpy.ops.object.transform_apply(location=True, scale=True, rotation=True) obj.select = False - - - - - - return {'FINISHED'} \ No newline at end of file + return {'FINISHED'} From f3d0cf3c5ed2c87b5ad4551a37475a0b6bc61ee6 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Thu, 20 Sep 2018 00:42:43 -0700 Subject: [PATCH 4/4] Update import_block_shape.py --- .../io_mesh_terasology/import_block_shape.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/blender_addons/io_mesh_terasology/import_block_shape.py b/blender_addons/io_mesh_terasology/import_block_shape.py index 1df7b82..04d4773 100644 --- a/blender_addons/io_mesh_terasology/import_block_shape.py +++ b/blender_addons/io_mesh_terasology/import_block_shape.py @@ -86,19 +86,17 @@ def execute(self, context): if(collider['type'] == 'AABB'): p = collider['position'] e = collider['extents'] - obj = bpy.ops.mesh.primitive_cube_add(location=(-p[0], p[1], p[2])) - obj.scale = (-e[0],e[1],e[2]) - obj.select = True - bpy.context.scene.objects.active = obj + bpy.ops.mesh.primitive_cube_add(location=(-p[0], p[1], p[2])) + bpy.ops.transform.resize(value=(-e[0], e[1], e[2])) bpy.ops.object.transform_apply(location=True, scale=True, rotation=True) - obj.select = False + obj = bpy.context.object + obj['teraColliderType'] = collider['type'] elif collider['type'] == 'Sphere': p = collider['position'] r = collider['radius'] - obj = bpy.ops.mesh.primitive_uv_sphere_add(segments=16,location = (-p[0], p[1], p[2]),size = r) - obj.select = True - bpy.context.scene.objects.active = obj + bpy.ops.mesh.primitive_uv_sphere_add(segments=16,location = (-p[0], p[1], p[2]),size = r) bpy.ops.object.transform_apply(location=True, scale=True, rotation=True) - obj.select = False + obj = bpy.context.object + obj['teraColliderType'] = collider['type'] return {'FINISHED'}