From 1df02cf6bf187dfc8f0afe9223dd0f4cbb90b559 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 9 Feb 2016 17:04:50 +0200 Subject: [PATCH] Add a Blender operator to export shapes --- blender/io_mspmath/__init__.py | 48 +++++++ blender/io_mspmath/export_shape.py | 195 +++++++++++++++++++++++++++++ blender/io_mspmath/outfile.py | 37 ++++++ blender/io_mspmath/properties.py | 19 +++ 4 files changed, 299 insertions(+) create mode 100644 blender/io_mspmath/__init__.py create mode 100644 blender/io_mspmath/export_shape.py create mode 100644 blender/io_mspmath/outfile.py create mode 100644 blender/io_mspmath/properties.py diff --git a/blender/io_mspmath/__init__.py b/blender/io_mspmath/__init__.py new file mode 100644 index 0000000..1490ce6 --- /dev/null +++ b/blender/io_mspmath/__init__.py @@ -0,0 +1,48 @@ +bl_info = { + "name": "Msp math datafiles", + "author": "Mikko Rasa", + "location": "File > Export", + "description": "Export Msp math data", + "category": "Import-Export" } + +if "bpy" in locals(): + import imp + for sub in "export_shape", "outfile", "properties": + if sub in locals(): + imp.reload(locals()[sub]) + +import bpy +from bpy_extras.io_utils import ExportHelper + +class ExportMspMathShape(bpy.types.Operator, ExportHelper): + bl_idname = "export_mesh.mspmath_shape" + bl_label = "Export Msp math shape" + + filename_ext = ".shape" + + def execute(self, context): + from .export_shape import ShapeExporter + exporter = ShapeExporter() + exporter.export(context, self.filepath) + return {"FINISHED"} + +def menu_func_export(self, context): + self.layout.operator(ExportMspMathShape.bl_idname, text="Msp math shape") + +from .properties import MspMathObjectProperties + +def register(): + bpy.utils.register_module(__name__) + + bpy.types.INFO_MT_file_export.append(menu_func_export) + + from .properties import register_properties + register_properties(); + +def unregister(): + bpy.utils.unregister_module(__name__) + + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__=="__main__": + register() diff --git a/blender/io_mspmath/export_shape.py b/blender/io_mspmath/export_shape.py new file mode 100644 index 0000000..793daae --- /dev/null +++ b/blender/io_mspmath/export_shape.py @@ -0,0 +1,195 @@ +import math +from mathutils import Vector + +class Vertex: + def __init__(self, v=None): + self.vertex = v + if v: + self.co = v.co + else: + self.co = Vector((0, 0, 0)) + +class Edge: + def __init__(self, e=None): + self.edge = e + self.vertices = [] + self.polygons = [] + self.convex = True + +class Polygon: + def __init__(self, p=None): + self.polygon = p + self.edges = [] + self.vertices = [] + if p: + self.normal = p.normal + else: + self.normal = Vector((0, 0, 1)) + +class Mesh: + def __init__(self, m=None): + if m: + self.vertices = [Vertex(v) for v in m.vertices] + self.edges = [Edge(e) for e in m.edges] + self.polygons = [Polygon(p) for p in m.polygons] + + for e in self.edges: + e.vertices = [self.vertices[i] for i in e.edge.vertices] + + for p in self.polygons: + p.vertices = [self.vertices[i] for i in p.polygon.vertices] + p.edges = [self.edges[m.loops[i].edge_index] for i in p.polygon.loop_indices] + for e in p.edges: + e.polygons.append(p) + + else: + self.edges = [] + self.vertices = [] + self.polygons = [] + self.convex = True + +class SphereFit: + def __init__(self, mesh): + self.mesh = mesh + min_coords = tuple(map(min, zip(*(v.co for v in self.mesh.vertices)))) + max_coords = tuple(map(max, zip(*(v.co for v in self.mesh.vertices)))) + self.center = (Vector(min_coords)+Vector(max_coords))/2 + self.radius = max(max_coords[i]-min_coords[i] for i in range(3))/2 + + def calculate_center_error(self): + error = Vector() + for v in self.mesh.vertices: + p = v.co-self.center + p.normalize() + p = self.center+p*self.radius + error += p-v.co + + return error/len(self.mesh.vertices) + + def calculate_radius_error(self): + error = 0 + for v in self.mesh.vertices: + error += self.radius-(v.co-self.center).length + return error/len(self.mesh.vertices) + + def calculate_fit_error(self): + error = 0 + for v in self.mesh.vertices: + error += (self.radius-(v.co-self.center).length)**2 + return math.sqrt(error/len(self.mesh.vertices)) + + def fit(self, epsilon=1e-3): + while 1: + error = self.calculate_radius_error() + if errordistances[side[0]][side[1]]: + side = (i, j) + + errors[side[0]][side[1]] += distances[side[0]][side[1]] + counts[side[0]][side[1]] += 1 + + for i in range(3): + for j in range(2): + if counts[i][j]: + errors[i][j] /= counts[i][j] + + return errors + + def calculate_fit_error(self): + error = 0 + for v in self.mesh.vertices: + distances = tuple((r[0]-v.co[i], v.co[i]-r[1]) for i, r in enumerate(self.ranges)) + error += max(max(*d) for d in distances)**2 + return math.sqrt(error/len(self.mesh.vertices)) + + def fit(self, epsilon=1e-3): + while 1: + errors = self.calculate_side_errors() + max_error = max(max(map(abs, e)) for e in errors) + if max_errorsphere.radius/1e5 + if use_center: + out_file.begin("transformed") + out_file.write("translate", *sphere.center) + + out_file.begin("sphere") + out_file.write("radius", sphere.radius) + out_file.end() + + if use_center: + out_file.end() + + elif shape_type=="BOX": + box = BoxFit(mesh) + error = box.fit() + + use_center = box.center.length>min(box.dimensions)/1e5 + if use_center: + out_file.begin("transformed") + out_file.write("translate", *box.center) + + out_file.begin("box") + out_file.write("dimensions", *box.dimensions) + out_file.end() + + if use_center: + out_file.end() diff --git a/blender/io_mspmath/outfile.py b/blender/io_mspmath/outfile.py new file mode 100644 index 0000000..2c747a8 --- /dev/null +++ b/blender/io_mspmath/outfile.py @@ -0,0 +1,37 @@ +import sys + +class OutFile: + def __init__(self, fn): + self.filename = fn + if fn==None: + self.file = sys.stdout + else: + self.file = open(fn, "w") + self.indent = 0 + + def make(self, kwd, *params): + pstr = "" + for p in params: + if type(p)==float: + pstr += " %#.6g"%p + else: + pstr += " %s"%p + return "%s%s"%(kwd, pstr) + + def write(self, kwd, *params): + self.file.write("%s%s;\n"%('\t'*self.indent, self.make(kwd, *params))) + + def begin(self, kwd, *params): + i = '\t'*self.indent + self.file.write("%s%s\n%s{\n"%(i, self.make(kwd, *params), i)) + self.indent += 1 + + def end(self): + self.indent -= 1 + self.file.write("%s};\n"%('\t'*self.indent)) + + +def open_output(f): + if isinstance(f, OutFile): + return f + return OutFile(f) diff --git a/blender/io_mspmath/properties.py b/blender/io_mspmath/properties.py new file mode 100644 index 0000000..b296e5a --- /dev/null +++ b/blender/io_mspmath/properties.py @@ -0,0 +1,19 @@ +import bpy + +class MspMathObjectProperties(bpy.types.Panel): + bl_idname = "OBJECT_PT_mspgl_properties" + bl_label = "MspGL properties" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + def draw(self, context): + obj = context.active_object + + self.layout.prop(obj, "shape_type"); + +def register_properties(): + bpy.types.Object.shape_type = bpy.props.EnumProperty(name="Shape type", description="Type of shape to use for exporting this object", default="AUTO", + items=(("AUTO", "Automatic", "Automatic"), + ("BOX", "Box", "Box"), + ("SPHERE", "Sphere", "Sphere"))) -- 2.43.0