<commit_before># -*- coding: utf-8 -*-
from . import pmx
from . import bpyutils

import collections

import mathutils
import bpy
import bmesh
import copy


class __PmxExporter:
    TO_PMX_MATRIX = mathutils.Matrix([
        [1.0, 0.0, 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 0.0, 1.0]])

    def __init__(self):
        self.__model = None
        self.__targetMesh = None

    @staticmethod
    def flipUV_V(uv):
        u, v = uv
        return [u, 1.0-v]

    @staticmethod
    def __getVertexGroupIndexToPmxBoneIndexMap(meshObj, nameMap):
        """ Create the dictionary to map vertex group indices to bone indices of the pmx.model instance.
        @param meshObj the object which has the target vertex groups.
        @param nameMap the dictionary to map Blender bone name to bone indices of the pmx.model instance.
        """
        r = {}
        for i in meshObj.vertex_groups:
            r[i.index] = nameMap.get(i.name, -1)
        return r

    @staticmethod
    def __getVerticesTable(mesh, vertexGroupToBoneIndexMap):
        """ Create the list which has pmx.Vertex instances.
        @param vertexGroupToBoneIndexMap the dictionary to map vertex group indices to bone indices of the pmx.model instance.
        """
        r = []
        for bv in mesh.vertices:
            pv = pmx.Vertex()
            pv.co = bv.co
            pv.normal = bv.normal * -1
            pv.uv = None

            t = len(bv.groups)
            if t == 0:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF1
                weight.bones = [-1]
                pv.weight = weight
            elif t == 1:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF1
                weight.bones = [vertexGroupToBoneIndexMap[bv.groups[0].group]]
                pv.weight = weight
            elif t == 2:
                vg1, vg2 = bv.groups
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF2
                weight.bones = [vertexGroupToBoneIndexMap[vg1.group], vertexGroupToBoneIndexMap[vg2.group]]
                weight.weights = [vg1.weight]
                pv.weight = weight
            else:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF4
                weight.bones = [-1, -1, -1, -1]
                weight.weights = [0.0, 0.0, 0.0, 0.0]
                for i in range(min(len(bv.groups), 4)):
                    vg = bv.groups[i]
                    weight.bones[i] = vertexGroupToBoneIndexMap[vg.group]
                    weight.weights[i] = vg.weight
                pv.weight = weight
            r.append(pv)
        return r

    @staticmethod
    def __getFaceTable(mesh, verticesTable):
        r = []
        for f, uv in zip(mesh.tessfaces, mesh.tessface_uv_textures.active.data):
            if len(f.vertices) != 3:
                raise Exception
            t = []
            for i in f.vertices:
                t.append(verticesTable[i])
            r.append((f, uv, t))
        return r

    @staticmethod
    def __convertFaceUVToVertexUV(vertex, uv, cloneVertexMap):
        if vertex.uv is None:
            vertex.uv = uv
        elif (vertex.uv[0] - uv[0])**2 + (vertex.uv[1] - uv[1])**2 > 0.0001:
            for i in cloneVertexMap[vertex]:
                if (i.uv[0] - uv[0])**2 + (i.uv[1] - uv[1])**2 < 0.0001:
                    return i
            n = copy.deepcopy(vertex)
            n.uv = uv
            if vertex not in cloneVertexMap:
                cloneVertexMap[vertex] = [n]
            else:
                cloneVertexMap[vertex].append(n)
            return n
        return vertex

    def __exportFaces(self, vertexTable, faceTable):
        materialIndexDict = collections.defaultdict(list)
        cloneVertexMap = {}
        for f, uv, vertices in faceTable:
            vertices[0] = self.__convertFaceUVToVertexUV(vertices[0], self.flipUV_V(uv.uv1), cloneVertexMap)
            vertices[1] = self.__convertFaceUVToVertexUV(vertices[1], self.flipUV_V(uv.uv2), cloneVertexMap)
            vertices[2] = self.__convertFaceUVToVertexUV(vertices[2], self.flipUV_V(uv.uv3), cloneVertexMap)

        verticesSet = set()
        for f, uv, vertices in faceTable:
            verticesSet.update(set(vertices))

        self.__model.vertices = list(verticesSet)
        invertMap = {}
        for i, v in enumerate(self.__model.vertices):
            invertMap[v] = i
        for f, uv, vertices in faceTable:
            v1 = invertMap[vertices[0]]
            v2 = invertMap[vertices[1]]
            v3 = invertMap[vertices[2]]
            materialIndexDict[f.material_index].append([v1, v2, v3])

        # make the map from vertex indices of Blender to them of pmx.
        vertexIndexMap = {}
        for i, v in enumerate(vertexTable):
            vertexIndexMap[i] = [invertMap[v]]
            if v in cloneVertexMap:
                for c in cloneVertexMap[v]:
                    vertexIndexMap[i].append(invertMap[c])

        for i in sorted(materialIndexDict.keys()):
            self.__model.faces.extend(materialIndexDict[i])
        return (materialIndexDict, vertexIndexMap)

    def __exportTexture(self, texture):
        if not isinstance(texture, bpy.types.ImageTexture):
            return -1
        t = pmx.Texture()
        t.path = texture.image.filepath
        self.__model.textures.append(t)
        return len(self.__model.textures) - 1

    def __exportMaterials(self, materialIndexDict):
        mesh = self.__targetMesh
        textureList = []
        for m_index, i in enumerate(mesh.materials):
            num_faces = len(materialIndexDict[m_index])
            if num_faces == 0:
                continue
            p_mat = pmx.Material()
            p_mat.name = i.name
            p_mat.name_e = i.name
            p_mat.diffuse = list(i.diffuse_color) + [i.alpha]
            p_mat.ambient = i.ambient_color or [0.5, 0.5, 0.5]
            p_mat.specular = list(i.specular_color) + [i.specular_alpha]
            p_mat.edge_color = [0.25, 0.3, 0.5, 0.5]
            p_mat.vertex_count = num_faces * 3
            if len(i.texture_slots) > 0:
                tex = i.texture_slots[0].texture
                index = -1
                if tex not in textureList:
                    index = self.__exportTexture(tex)
                    textureList.append(tex)
                else:
                    index = textureList.index(tex)
                p_mat.texture = index
                p_mat.diffuse[3] = 1.0 # Set the alpha value to 1.0 if the material has textures.
            self.__model.materials.append(p_mat)

    def __exportBones(self):
        """ Export bones.
        @return the dictionary to map Blender bone names to bone indices of the pmx.model instance.
        """
        arm = self.__armature<commit_msg>Fix the bug that causes export errors of materials that have no texture.<commit_after># -*- coding: utf-8 -*-
from . import pmx
from . import bpyutils

import collections
import os
import copy

import mathutils
import bpy
import bmesh


class __PmxExporter:
    TO_PMX_MATRIX = mathutils.Matrix([
        [1.0, 0.0, 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 0.0, 1.0]])

    def __init__(self):
        self.__model = None
        self.__targetMesh = None

    @staticmethod
    def flipUV_V(uv):
        u, v = uv
        return [u, 1.0-v]

    @staticmethod
    def __getVertexGroupIndexToPmxBoneIndexMap(meshObj, nameMap):
        """ Create the dictionary to map vertex group indices to bone indices of the pmx.model instance.
        @param meshObj the object which has the target vertex groups.
        @param nameMap the dictionary to map Blender bone name to bone indices of the pmx.model instance.
        """
        r = {}
        for i in meshObj.vertex_groups:
            r[i.index] = nameMap.get(i.name, -1)
        return r

    @staticmethod
    def __getVerticesTable(mesh, vertexGroupToBoneIndexMap):
        """ Create the list which has pmx.Vertex instances.
        @param vertexGroupToBoneIndexMap the dictionary to map vertex group indices to bone indices of the pmx.model instance.
        """
        r = []
        for bv in mesh.vertices:
            pv = pmx.Vertex()
            pv.co = bv.co
            pv.normal = bv.normal * -1
            pv.uv = None

            t = len(bv.groups)
            if t == 0:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF1
                weight.bones = [-1]
                pv.weight = weight
            elif t == 1:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF1
                weight.bones = [vertexGroupToBoneIndexMap[bv.groups[0].group]]
                pv.weight = weight
            elif t == 2:
                vg1, vg2 = bv.groups
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF2
                weight.bones = [vertexGroupToBoneIndexMap[vg1.group], vertexGroupToBoneIndexMap[vg2.group]]
                weight.weights = [vg1.weight]
                pv.weight = weight
            else:
                weight = pmx.BoneWeight()
                weight.type = pmx.BoneWeight.BDEF4
                weight.bones = [-1, -1, -1, -1]
                weight.weights = [0.0, 0.0, 0.0, 0.0]
                for i in range(min(len(bv.groups), 4)):
                    vg = bv.groups[i]
                    weight.bones[i] = vertexGroupToBoneIndexMap[vg.group]
                    weight.weights[i] = vg.weight
                pv.weight = weight
            r.append(pv)
        return r

    @staticmethod
    def __getFaceTable(mesh, verticesTable):
        r = []
        for f, uv in zip(mesh.tessfaces, mesh.tessface_uv_textures.active.data):
            if len(f.vertices) != 3:
                raise Exception
            t = []
            for i in f.vertices:
                t.append(verticesTable[i])
            r.append((f, uv, t))
        return r

    @staticmethod
    def __convertFaceUVToVertexUV(vertex, uv, cloneVertexMap):
        if vertex.uv is None:
            vertex.uv = uv
        elif (vertex.uv[0] - uv[0])**2 + (vertex.uv[1] - uv[1])**2 > 0.0001:
            for i in cloneVertexMap[vertex]:
                if (i.uv[0] - uv[0])**2 + (i.uv[1] - uv[1])**2 < 0.0001:
                    return i
            n = copy.deepcopy(vertex)
            n.uv = uv
            if vertex not in cloneVertexMap:
                cloneVertexMap[vertex] = [n]
            else:
                cloneVertexMap[vertex].append(n)
            return n
        return vertex

    def __exportFaces(self, vertexTable, faceTable):
        materialIndexDict = collections.defaultdict(list)
        cloneVertexMap = {}
        for f, uv, vertices in faceTable:
            vertices[0] = self.__convertFaceUVToVertexUV(vertices[0], self.flipUV_V(uv.uv1), cloneVertexMap)
            vertices[1] = self.__convertFaceUVToVertexUV(vertices[1], self.flipUV_V(uv.uv2), cloneVertexMap)
            vertices[2] = self.__convertFaceUVToVertexUV(vertices[2], self.flipUV_V(uv.uv3), cloneVertexMap)

        verticesSet = set()
        for f, uv, vertices in faceTable:
            verticesSet.update(set(vertices))

        self.__model.vertices = list(verticesSet)
        invertMap = {}
        for i, v in enumerate(self.__model.vertices):
            invertMap[v] = i
        for f, uv, vertices in faceTable:
            v1 = invertMap[vertices[0]]
            v2 = invertMap[vertices[1]]
            v3 = invertMap[vertices[2]]
            materialIndexDict[f.material_index].append([v1, v2, v3])

        # make the map from vertex indices of Blender to them of pmx.
        vertexIndexMap = {}
        for i, v in enumerate(vertexTable):
            vertexIndexMap[i] = [invertMap[v]]
            if v in cloneVertexMap:
                for c in cloneVertexMap[v]:
                    vertexIndexMap[i].append(invertMap[c])

        for i in sorted(materialIndexDict.keys()):
            self.__model.faces.extend(materialIndexDict[i])
        return (materialIndexDict, vertexIndexMap)

    def __exportTexture(self, texture):
        if not isinstance(texture, bpy.types.ImageTexture):
            return -1
        t = pmx.Texture()
        t.path = texture.image.filepath
        self.__model.textures.append(t)
        if not os.path.isfile(t.path):
            logging.warning('  The texture file does not exist: %s', t.path)
        return len(self.__model.textures) - 1

    def __exportMaterials(self, materialIndexDict):
        mesh = self.__targetMesh
        textureList = []
        for m_index, i in enumerate(mesh.materials):
            num_faces = len(materialIndexDict[m_index])
            if num_faces == 0:
                continue
            p_mat = pmx.Material()
            p_mat.name = i.name
            p_mat.name_e = i.name
            p_mat.diffuse = list(i.diffuse_color) + [i.alpha]
            p_mat.ambient = i.ambient_color or [0.5, 0.5, 0.5]
            p_mat.specular = list(i.specular_color) + [i.specular_alpha]
            p_mat.edge_color = [0.25, 0.3, 0.5, 0.5]
            p_mat.vertex_count = num_faces * 3
            if len(i.texture_slots) > 0 and i.texture_slots[0] is not None:
                tex = i.texture_slots[0].texture
                index = -1
                if tex not in textureList:
                    index = self.__exportTexture(tex)
                    textureList.append(tex)
                else:
                    index = textureList.index(tex)
                p_mat.texture = index
                p_mat.diffuse[3] = 1.0 # Set the alpha value to 1.0 if the material has textures.
            self.__model.materials.append(p_mat)

    def __exportBones(self):
        """ Export bones.
        @return the dictionary to map Blender bone names to bone indices of the pmx.model instance.
        """
        arm = self.__armature
{'fraction_non_alphanumeric': 0.10312278211497516, 'fraction_numerical': 0.018807665010645847, 'mean_word_length': 1.7014953987730062, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1169489', 'n_tokens_mistral': 4519, 'n_tokens_neox': 4236, 'n_words': 1042}