0

Given a Blender (ver 2.9) model that is made up of separate objects, each of which is a triangle, I have a script which exports them to a custom format that includes the position, euler rotation and vertices of each triangle.

Because my target program rotates the triangle using the specified object euler angles, I need to 'flatten' the vertices of each triangle into just XY coordinates, as if the triangle was 2D.

This is my first Blender script and first Python script, so it's very basic without any error-checking. Assume that my objects are definitely all triangles :) It currently exports the vertices 'as-is' in 3D.

bl_info = {
    "name": "Dave's Export",
    "author": "Dave",
    "version": (1, 2),
    "blender": (2, 83, 0),
    "location": "File > Export > Dave's Export",
    "warning": "",
    "description": "Export active mesh",
    "category": "Import-Export",
}

import bpy
import os

from bpy_extras.io_utils import ExportHelper

class DavesExport(bpy.types.Operator, ExportHelper):

    bl_idname = "export_scene.custom_export"
    bl_label = "Export"
    bl_options = {'PRESET', 'UNDO'}
    filename_ext = ".dave"

    def execute(self, context):

        filepath = self.filepath
        filepath = bpy.path.ensure_ext(filepath, self.filename_ext)

        out = open(filepath, "w")
        scene = bpy.data.scenes[0]

        for obj in scene.objects:

            if obj.type == 'MESH':

                # Note; obj is just a triangle.

                # Position
                out.write(str(obj.location.x) + ' ' + str(obj.location.y) + ' ' + str(obj.location.z))
                out.write('|')

                # Rotation
                out.write(str(obj.rotation_euler.x) + ' ' + str(obj.rotation_euler.y) + ' ' + str(obj.rotation_euler.z))
                out.write('|')

                # Vertices (x3)
                for vertex in obj.data.vertices:

                    out.write(str(vertex.co.x) + ' ' + str(vertex.co.y) + ' ' + str(vertex.co.z))
                    out.write('|')

                out.write('\n')

        out.close()

        return {'FINISHED'}

    # Constructor (?)

    def invoke(self, context, event):

        wm = context.window_manager
        wm.fileselect_add(self)

        return {'RUNNING_MODAL'}

# This function adds an item to the export menu.

def menu_button(self, context):

    self.layout.operator(DavesExport.bl_idname, text="Dave's Export")

# Executes on Blender startup (?)

def register():

    bpy.utils.register_class(DavesExport)
    bpy.types.TOPBAR_MT_file_export.append(menu_button)

# Executes on Blender exit (?)

def unregister():

    bpy.utils.unregister_class(DavesExport)
    bpy.types.TOPBAR_MT_file_export.remove(menu_button)

(Additionally, I hope this script represents a simple exporter for use in 2.9 so that people can learn from it - it took me an entire day to get this working as there's not a lot of up-to-date information on this)

whoshotdk
  • 286
  • 2
  • 14

1 Answers1

0

I'm not quite sure what your goal is but to "flatten" the vertices sounds like a projection operation where you need to transform the vertex positions so Z equals zero. For that you could calculate the face orientation of the triangle and rotate the triangle vertices by the negative of that. But then it is also unclear what the significance of the local offset is around which that rotation would takes place. You could try to elaborate on the result you want to achieve, the source data you're working with or your attempts on it so far since the solution might as well be just ignoring the Z value of the triangle vertices when exporting if they're already provided aligned flat to the plane of the X and Y axis.

Adding answers from the comments below:

1: To rotate the triangle around its center point P where P is the average position of the 3 vertices (V0+V1+V2)/3 and the rotation R is derived from the face normal which is calculated with the dot product of the vectors V1-V0 and V2-V0 as elaborated here https://stackoverflow.com/a/1516330/15786521 the vertices need to moved by the negative of P, rotated by the negative of R and then moved back by P or in matrix operations where V is a vertex position:

V' = V x M(-P) x M(-R) x M(P)

The problem though is that rotations don't commute, meaning that the outcome of rotating around the X and then by the Y axis is not the same as rotating it around Y and then by X. In your case rotating around the yaw, then pitch, and ignoring the roll might work but I can not tell which way would best suit you since the resulting roll of the triangle would differ if you rotated by the pitch first for instance.

2: If you simply want to map your triangle on a texture or the like where only the proportions matter and the 3d orientation is irrelevant since the goal seems to neutralize/flatten it then simply get the dot product and distances of the vectors V1-V0 and V2-V0 and construct your 2D representation of the triangle from that.

krysov
  • 116
  • 1
  • 4
  • You are correct in that I'd like to project to make Z = 0. Incoming data is that as provided by Blender's API. I am currently ignoring the Z in my target program, but this renders the model incorrectly; it generally appears flat. The triangles would be rotated about their centers. As for which way to rotate them (which axis first?) - I guess I need to define a local 'forward' direction for each triangle, say -Y is forward, -X is left. Does that info help? I shall try your suggestion of 'negative orientation' - is that just the negative face normal? Thank you for your answer! – whoshotdk Jul 17 '21 at 13:01
  • 1
    Alright, then the pivot is now known which is P = [(V0.x + V1.x + V2.x)/3, (V0.y + V1.y + V2.y)/3, (V0.z + V1.z + V2.z)/3] that is the average position of all 3 vertices. Now you need to subtract that from each vertice, perform the rotation where R is derived from the face normal, and then put it back. With matrices this would look like V' = V x M(-P) x M(-R) x M(P) although I do not know how you need it to be rotated. You might rotate it by the yaw, then pitch and ignore the roll. – krysov Jul 17 '21 at 17:11
  • 1
    Although if you simply seek to map it to a spread or texture sheet you could simply get the dot products and distances of V0 x V1 and V0 x V2 and derive your 2D representation of the triangle from that. – krysov Jul 17 '21 at 17:13
  • That's really helpful! I'll add your comments to your answer and mark it as accepted once I've got things working, which I'm quite confident of now, thank you. – whoshotdk Jul 17 '21 at 17:22
  • Ah, "the suggested edit queue is full". Perhaps you could edit your answer to include them? – whoshotdk Jul 17 '21 at 17:24