示例#1
0
文件: model.py 项目: blank63/j3dview
class SceneGraph(WrapperModel):
    unknown0 = _attribute()
    children = _attribute(_list(SceneGraphNode.create_node))

    def all_nodes(self):
        for child in self.children:
            yield child
            yield from child.all_descendants()
示例#2
0
class Material(WrapperModel):

    block_info = BlockInfo()
    shader_info = ShaderInfo()

    def __init__(self, wrapped_object):
        super().__init__(wrapped_object)
        self.gl_program_table = {}

    name = _attribute()
    unknown0 = _attribute()
    cull_mode = _attribute()

    channel_count = _attribute()
    channels = _attribute(_list(Channel))

    texcoord_generator_count = _attribute()
    texcoord_generators = _attribute(_list(TexCoordGenerator))
    texture_matrices = _attribute(_list(TextureMatrix))
    textures = ReferenceAttribute()

    tev_stage_count = _attribute()
    tev_stages = _attribute(_list(TevStage))
    tev_colors = _attribute(_list())
    tev_color_previous = _attribute()
    kcolors = _attribute(_list())
    swap_tables = _attribute(_list(SwapTable))

    indirect_stage_count = _attribute()
    indirect_stages = _attribute(_list(IndirectStage))
    indirect_matrices = _attribute(_list(IndirectMatrix))

    alpha_test = _attribute(AlphaTest)
    fog = _attribute()
    depth_test_early = _attribute()
    depth_mode = _attribute(DepthMode)
    blend_mode = _attribute(BlendMode)
    dither = _attribute()

    @property
    def enabled_channels(self):
        for i in range(self.channel_count):
            yield self.channels[i]

    @property
    def enabled_texcoord_generators(self):
        for i in range(self.texcoord_generator_count):
            yield self.texcoord_generators[i]

    @property
    def enabled_tev_stages(self):
        for i in range(self.tev_stage_count):
            yield self.tev_stages[i]

    @property
    def enabled_indirect_stages(self):
        for i in range(self.indirect_stage_count):
            yield self.indirect_stages[i]

    def handle_event(self, event, path):
        if isinstance(event, ValueChangedEvent):
            if path in self.block_info.trigger_table:
                block_property = self.block_info.trigger_table[path]
                block_property.update_block(self.gl_block, self)
            if path in self.shader_info.triggers:
                self.gl_shader_invalidate()
        super().handle_event(event, path)

    @LazyProperty
    def gl_block(self):
        block = self.gl_create_resource(self.block_info.block_type,
                                        GL_DYNAMIC_DRAW)
        for block_property in self.block_info.properties:
            block_property.update_block(block, self)
        return block

    def gl_program(self, transformation_type):
        if transformation_type in self.gl_program_table:
            return self.gl_program_table[transformation_type]

        vertex_shader_string = models.vertex_shader.create_shader_string(
            self, transformation_type)
        fragment_shader_string = models.fragment_shader.create_shader_string(
            self)
        vertex_shader = self.gl_create_resource(gl.Shader, GL_VERTEX_SHADER,
                                                vertex_shader_string)
        fragment_shader = self.gl_create_resource(gl.Shader,
                                                  GL_FRAGMENT_SHADER,
                                                  fragment_shader_string)
        program = self.gl_create_resource(gl.Program, vertex_shader,
                                          fragment_shader)
        self.gl_delete_resource(vertex_shader)
        self.gl_delete_resource(fragment_shader)

        glUseProgram(program)

        matrix_block_index = glGetUniformBlockIndex(program, b'MatrixBlock')
        glUniformBlockBinding(program, matrix_block_index,
                              MATRIX_BLOCK_BINDING_POINT)

        material_block_index = glGetUniformBlockIndex(program,
                                                      b'MaterialBlock')
        if material_block_index != GL_INVALID_INDEX:
            glUniformBlockBinding(program, material_block_index,
                                  MATERIAL_BLOCK_BINDING_POINT)

        program.matrix_index_location = glGetUniformLocation(
            program, 'matrix_index')  #<-?

        matrix_table_location = glGetUniformLocation(program, 'matrix_table')
        if matrix_table_location != -1:
            glUniform1i(matrix_table_location, MATRIX_TABLE_TEXTURE_UNIT)

        for i in range(8):
            location = glGetUniformLocation(program, 'texmap{}'.format(i))
            if location == -1: continue
            glUniform1i(location, TEXTURE_UNITS[i])

        self.gl_program_table[transformation_type] = program
        return program

    def gl_shader_invalidate(self):
        for program in self.gl_program_table.values():
            self.gl_delete_resource(program)
        self.gl_program_table.clear()

    @property
    def gl_cull_mode(self):
        if self.cull_mode == gx.CULL_FRONT:
            return GL_FRONT
        if self.cull_mode == gx.CULL_BACK:
            return GL_BACK
        if self.cull_mode == gx.CULL_ALL:
            return GL_FRONT_AND_BACK
        raise ValueError('Invalid cull mode: {}'.format(self.cull_mode))

    @property
    def gl_depth_function(self):
        if self.depth_mode.function == gx.NEVER:
            return GL_NEVER
        if self.depth_mode.function == gx.LESS:
            return GL_LESS
        if self.depth_mode.function == gx.EQUAL:
            return GL_EQUAL
        if self.depth_mode.function == gx.LEQUAL:
            return GL_LEQUAL
        if self.depth_mode.function == gx.GREATER:
            return GL_GREATER
        if self.depth_mode.function == gx.NEQUAL:
            return GL_NOTEQUAL
        if self.depth_mode.function == gx.GEQUAL:
            return GL_GEQUAL
        if self.depth_mode.function == gx.ALWAYS:
            return GL_ALWAYS
        raise ValueError('Invalid compare function: {}'.format(
            self.depth_mode.function))

    @property
    def gl_blend_source_factor(self):
        if self.blend_mode.source_factor == gx.BL_ZERO:
            return GL_ZERO
        if self.blend_mode.source_factor == gx.BL_ONE:
            return GL_ONE
        if self.blend_mode.source_factor == gx.BL_SRCALPHA:
            return GL_SRC_ALPHA
        if self.blend_mode.source_factor == gx.BL_INVSRCALPHA:
            return GL_ONE_MINUS_SRC_ALPHA
        if self.blend_mode.source_factor == gx.BL_DSTALPHA:
            return GL_DST_ALPHA
        if self.blend_mode.source_factor == gx.BL_INVDSTALPHA:
            return GL_ONE_MINUS_DST_ALPHA
        if self.blend_mode.source_factor == gx.BL_DSTCLR:
            return GL_DST_COLOR
        if self.blend_mode.source_factor == gx.BL_INVDSTCLR:
            return GL_ONE_MINUS_DST_COLOR
        raise ValueError('Invalid blend source factor: {}'.format(
            self.blend_mode.source_factor))

    @property
    def gl_blend_destination_factor(self):
        if self.blend_mode.destination_factor == gx.BL_ZERO:
            return GL_ZERO
        if self.blend_mode.destination_factor == gx.BL_ONE:
            return GL_ONE
        if self.blend_mode.destination_factor == gx.BL_SRCALPHA:
            return GL_SRC_ALPHA
        if self.blend_mode.destination_factor == gx.BL_INVSRCALPHA:
            return GL_ONE_MINUS_SRC_ALPHA
        if self.blend_mode.destination_factor == gx.BL_DSTALPHA:
            return GL_DST_ALPHA
        if self.blend_mode.destination_factor == gx.BL_INVDSTALPHA:
            return GL_ONE_MINUS_DST_ALPHA
        if self.blend_mode.destination_factor == gx.BL_SRCCLR:
            return GL_SRC_COLOR
        if self.blend_mode.destination_factor == gx.BL_INVSRCCLR:
            return GL_ONE_MINUS_SRC_COLOR
        raise ValueError('Invalid blend destination factor: {}'.format(
            self.blend_mode.destination_factor))

    @property
    def gl_blend_logical_operation(self):
        if self.blend_mode.logical_operation == gx.LO_CLEAR:
            return GL_CLEAR
        if self.blend_mode.logical_operation == gx.LO_AND:
            return GL_AND
        if self.blend_mode.logical_operation == gx.LO_REVAND:
            return GL_AND_REVERSE
        if self.blend_mode.logical_operation == gx.LO_COPY:
            return GL_COPY
        if self.blend_mode.logical_operation == gx.LO_INVAND:
            return GL_AND_INVERTED
        if self.blend_mode.logical_operation == gx.LO_NOOP:
            return GL_NOOP
        if self.blend_mode.logical_operation == gx.LO_XOR:
            return GL_XOR
        if self.blend_mode.logical_operation == gx.LO_OR:
            return GL_OR
        if self.blend_mode.logical_operation == gx.LO_NOR:
            return GL_NOR
        if self.blend_mode.logical_operation == gx.LO_EQUIV:
            return GL_EQUIV
        if self.blend_mode.logical_operation == gx.LO_INV:
            return GL_INVERT
        if self.blend_mode.logical_operation == gx.LO_REVOR:
            return GL_OR_INVERTED
        if self.blend_mode.logical_operation == gx.LO_INVCOPY:
            return GL_COPY_INVERTED
        if self.blend_mode.logical_operation == gx.LO_INVOR:
            return GL_OR_INVERTED
        if self.blend_mode.logical_operation == gx.LO_INVNAND:
            return GL_NAND
        if self.blend_mode.logical_operation == gx.LO_SET:
            return GL_SET
        raise ValueError('Invalid logical operation: {}'.format(
            self.blend_mode.logical_operation))

    def gl_bind(self, shape):
        self.gl_block.bind(MATERIAL_BLOCK_BINDING_POINT)

        for i, texture in enumerate(self.textures):
            if texture is None:
                continue
            texture.gl_bind(TEXTURE_UNITS[i])

        if self.cull_mode != gx.CULL_NONE:
            glEnable(GL_CULL_FACE)
            glCullFace(self.gl_cull_mode)
        else:
            glDisable(GL_CULL_FACE)

        if self.depth_mode.enable:
            glEnable(GL_DEPTH_TEST)
            glDepthFunc(self.gl_depth_function)
            glDepthMask(self.depth_mode.update_enable)
        else:
            glDisable(GL_DEPTH_TEST)

        if self.blend_mode.function == gx.BM_BLEND:
            glEnable(GL_BLEND)
            glBlendEquation(GL_FUNC_ADD)
            glBlendFunc(self.gl_blend_source_factor,
                        self.gl_blend_destination_factor)
        elif self.blend_mode.function == gx.BM_SUBTRACT:
            glEnable(GL_BLEND)
            glBlendEquation(GL_FUNC_REVERSE_SUBTRACT)
            glBlendFunc(GL_ONE, GL_ONE)
        else:
            glDisable(GL_BLEND)

        if self.blend_mode.function == gx.BM_LOGIC:
            glEnable(GL_COLOR_LOGIC_OP)
            glLogicOp(self.gl_blend_logical_operation)
        else:
            glDisable(GL_COLOR_LOGIC_OP)

        if self.dither:
            glEnable(GL_DITHER)
        else:
            glDisable(GL_DITHER)

        program = self.gl_program(shape.transformation_type)

        glUseProgram(program)

        if shape.transformation_type == 0:
            glUniform1i(program.matrix_index_location,
                        shape.batches[0].matrix_table[0])

    def gl_delete(self):
        super().gl_delete()
        try:
            del self.gl_block
        except AttributeError:
            pass
        self.gl_program_table.clear()
示例#3
0
文件: model.py 项目: blank63/j3dview
class Model(WrapperModel):
    def __init__(self, wrapped_object):
        super().__init__(wrapped_object)
        self.file_path = None
        self.init_references()

    file_type = _attribute()
    subversion = _attribute()
    scene_graph = _attribute(SceneGraph)
    position_array = _attribute()
    normal_array = _attribute()
    color_arrays = _attribute()
    texcoord_arrays = _attribute()
    influence_groups = _attribute()
    inverse_bind_matrices = _attribute()
    matrix_definitions = _attribute()
    joints = _attribute()
    shapes = _attribute(_list(models.shape.Shape))
    materials = _attribute(_list(models.material.Material))
    textures = _attribute(_list(models.texture.Texture))

    def gl_init(self):
        array_table = {}
        array_table[gx.VA_PTNMTXIDX] = GLMatrixIndexArray()
        array_table.update({
            attribute: GLDirectArray(attribute)
            for attribute in gx.VA_TEXMTXIDX
        })
        array_table[gx.VA_POS] = gl_convert_array(self.position_array)
        array_table[gx.VA_NRM] = gl_convert_array(self.normal_array)
        array_table.update({
            attribute: gl_convert_color_array(array)
            for attribute, array in zip(gx.VA_CLR, self.color_arrays)
        })
        array_table.update({
            attribute: gl_convert_array(array)
            for attribute, array in zip(gx.VA_TEX, self.texcoord_arrays)
        })

        for shape in self.shapes:
            shape.gl_init(array_table)

        self.gl_joints = [copy.copy(joint) for joint in self.joints]
        self.gl_joint_matrices = numpy.empty((len(self.joints), 3, 4),
                                             numpy.float32)
        self.gl_matrix_table = self.gl_create_resource(
            gl.TextureBuffer, GL_DYNAMIC_DRAW, GL_RGBA32F,
            (len(self.matrix_definitions), 3, 4), numpy.float32)
        self.gl_update_matrix_table()

    def gl_update_joint_matrices(self,
                                 node,
                                 parent_joint=None,
                                 parent_joint_matrix=numpy.eye(
                                     3, 4, dtype=numpy.float32)):
        for child in node.children:
            if child.node_type == NodeType.JOINT:
                joint = self.gl_joints[child.index]
                joint_matrix = self.gl_joint_matrices[child.index]
                joint_matrix[:] = joint.create_matrix(parent_joint,
                                                      parent_joint_matrix)
                self.gl_update_joint_matrices(child, joint, joint_matrix)
            else:
                self.gl_update_joint_matrices(child, parent_joint,
                                              parent_joint_matrix)

    def gl_update_matrix_table(self):
        self.gl_update_joint_matrices(self.scene_graph)

        if self.inverse_bind_matrices is not None:
            influence_matrices = matrix3x4_array_multiply(
                self.gl_joint_matrices, self.inverse_bind_matrices)

        for matrix, matrix_definition in zip(self.gl_matrix_table,
                                             self.matrix_definitions):
            if matrix_definition.matrix_type == MatrixType.JOINT:
                matrix[:] = self.gl_joint_matrices[matrix_definition.index]
            elif matrix_definition.matrix_type == MatrixType.INFLUENCE_GROUP:
                influence_group = self.influence_groups[
                    matrix_definition.index]
                matrix[:] = sum(influence.weight *
                                influence_matrices[influence.index]
                                for influence in influence_group)
            else:
                ValueError('invalid matrix type')

    def gl_draw_shape(self, material, shape):
        if shape.gl_hide: return
        material.gl_bind(shape)
        shape.gl_bind()
        shape.gl_draw()

    def gl_draw_node(self, node, parent_material=None):
        for child in node.children:
            if child.node_type == NodeType.SHAPE:
                if parent_material.unknown0 == 1:
                    self.gl_draw_shape(parent_material,
                                       self.shapes[child.index])
                self.gl_draw_node(child, parent_material)
                if parent_material.unknown0 == 4:
                    self.gl_draw_shape(parent_material,
                                       self.shapes[child.index])
            elif child.node_type == NodeType.MATERIAL:
                self.gl_draw_node(child, child.material)
            else:
                self.gl_draw_node(child, parent_material)

    def gl_draw(self):
        self.gl_matrix_table.bind_texture(
            models.material.MATRIX_TABLE_TEXTURE_UNIT)
        self.gl_draw_node(self.scene_graph)

    @staticmethod
    def load(file_path):
        with open(file_path, 'rb') as stream:
            model = j3d.model.unpack(stream)
        model = Model(model)
        model.file_path = file_path
        return model

    def save(self, file_path):
        self.file_path = file_path
        self.sync_reference_indices()
        with open(file_path, 'wb') as stream:
            j3d.model.pack(stream, self.wrapped_object)

    def init_references(self):
        """Initialize references.

        Initialize references into the material and texture lists.
        """
        for node in self.scene_graph.all_nodes():
            if node.node_type == NodeType.MATERIAL:
                node.material = self.materials[node.wrapped_object.index]

        for material in self.materials:
            material.textures = ReferenceList(
                self.
                textures[texture_index] if texture_index is not None else None
                for texture_index in material.wrapped_object.texture_indices)

    def sync_reference_indices(self):
        """Synchronize reference indices.

        Indices used to reference into the material and texture lists are not
        automatically kept in sync. This method needs to be manually called to
        synchronize the reference indices.
        """
        for node in self.scene_graph.all_nodes():
            if node.node_type == NodeType.MATERIAL:
                node.wrapped_object.index = self.materials.index(node.material)

        for material in self.materials:
            for i, texture in enumerate(material.textures):
                texture_index = None
                if texture is not None:
                    texture_index = self.textures.index(texture)
                material.wrapped_object.texture_indices[i] = texture_index

    def get_nodes_using_material(self, material_index):
        """Get scene graph nodes that use a given material.

        :param material_index: Index of the material in the material list.
        :return: List of the scene graph nodes that use the material.
        """
        material = self.materials[material_index]
        nodes = []
        for node in self.scene_graph.all_nodes():
            if node.node_type == NodeType.MATERIAL and node.material == material:
                nodes.append(node)
        return nodes

    def get_materials_using_texture(self, texture_index):
        """Get materials that use a given texture.

        :param texture_index: Index of the texture in the texture list.
        :return: List of the materials that use the texture.
        """
        texture = self.textures[texture_index]
        materials = []
        for material in self.materials:
            if texture in material.textures:
                materials.append(material)
                continue
        return materials
示例#4
0
文件: model.py 项目: blank63/j3dview
class ShapeNode(SceneGraphNode):
    node_type = _attribute()
    index = _attribute()
    children = _attribute(_list(SceneGraphNode.create_node))
示例#5
0
文件: model.py 项目: blank63/j3dview
class MaterialNode(SceneGraphNode):
    node_type = _attribute()
    material = ReferenceAttribute()
    children = _attribute(_list(SceneGraphNode.create_node))