# A Blender add-on: File > Export > 3DOpera model (.glb). It exports what's selected, or
# everything, with modifiers applied and faces triangulated, then says whether it fits the
# console. Take the .glb to 3DOpera's Create page, under 3D model.
#
# Install: Edit > Preferences > Add-ons > Install from Disk, and choose this file.
# Or with no window: blender --background scene.blend --python io_3dopera.py -- out.glb
import sys

import bpy
from bpy.props import BoolProperty, StringProperty
from bpy_extras.io_utils import ExportHelper

bl_info = {
    "name": "3DOpera model (.glb)",
    "author": "3DOpera",
    "version": (1, 0, 0),
    "blender": (3, 6, 0),
    "location": "File > Export",
    "description": "Exports a mesh and its texture for 3DOpera's Create page",
    "category": "Import-Export",
}

# The console's player takes this many faces, counting a flat pair of triangles as one.
MOST_TRIANGLES = 2048
# Textures are cut to 8 to 32 pixels a face, so larger ones are wasted.
LARGEST_USEFUL_TEXTURE = 512


def count_triangles(objects):
    graph = bpy.context.evaluated_depsgraph_get()
    total = 0
    for item in objects:
        if item.type != "MESH":
            continue
        mesh = item.evaluated_get(graph).to_mesh()
        mesh.calc_loop_triangles()
        total += len(mesh.loop_triangles)
        item.evaluated_get(graph).to_mesh_clear()
    return total


def large_textures(objects):
    names = set()
    for item in objects:
        if item.type != "MESH":
            continue
        for slot in item.material_slots:
            if slot.material and slot.material.use_nodes:
                for node in slot.material.node_tree.nodes:
                    if node.type == "TEX_IMAGE" and node.image and max(node.image.size) > LARGEST_USEFUL_TEXTURE:
                        names.add(node.image.name)
    return sorted(names)


def export(path, selected_only):
    objects = bpy.context.selected_objects if selected_only else bpy.context.scene.objects
    triangles = count_triangles(objects)
    bpy.ops.export_scene.gltf(filepath=path, export_format="GLB", use_selection=selected_only, export_apply=True, export_yup=True, export_animations=False, export_cameras=False, export_lights=False)
    notes = [f"{triangles} triangles."]
    if triangles > MOST_TRIANGLES:
        notes.append(f"That's more than the console's player takes, about {MOST_TRIANGLES}. Try a Decimate modifier.")
    big = large_textures(objects)
    if big:
        notes.append(f"Larger textures than needed: {', '.join(big)}.")
    return " ".join(notes)


class Export3DOpera(bpy.types.Operator, ExportHelper):
    """Export a mesh and its texture for 3DOpera"""

    bl_idname = "export_scene.opera3do"
    bl_label = "Export 3DOpera model"
    filename_ext = ".glb"
    filter_glob: StringProperty(default="*.glb", options={"HIDDEN"})
    selected_only: BoolProperty(name="Selected only", default=False)

    def execute(self, context):
        self.report({"INFO"}, export(self.filepath, self.selected_only))
        return {"FINISHED"}


def menu_item(self, _context):
    self.layout.operator(Export3DOpera.bl_idname, text="3DOpera model (.glb)")


def register():
    bpy.utils.register_class(Export3DOpera)
    bpy.types.TOPBAR_MT_file_export.append(menu_item)


def unregister():
    bpy.types.TOPBAR_MT_file_export.remove(menu_item)
    bpy.utils.unregister_class(Export3DOpera)


if __name__ == "__main__":
    if "--" in sys.argv:
        print(export(sys.argv[sys.argv.index("--") + 1], False))
    else:
        register()
