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()
class Shape(WrapperModel): transformation_type = _attribute() batches = _attribute() attribute_descriptors = _attribute() @property def attributes(self): for descriptor in self.attribute_descriptors: yield descriptor.attribute @property def primitives(self): for batch in self.batches: yield from batch.primitives def gl_init(self, array_table): self.gl_hide = False self.gl_vertex_array = self.gl_create_resource(gl.VertexArray) glBindVertexArray(self.gl_vertex_array) self.gl_vertex_buffer = self.gl_create_resource(gl.Buffer) glBindBuffer(GL_ARRAY_BUFFER, self.gl_vertex_buffer) self.gl_element_count = 3 * gl_count_triangles(self) self.gl_element_buffer = self.gl_create_resource(gl.Buffer) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.gl_element_buffer) vertex_type = numpy.dtype( [array_table[attribute].field() for attribute in self.attributes]) vertex_count = sum( len(primitive.vertices) for primitive in self.primitives) vertex_array = numpy.empty(vertex_count, vertex_type) for attribute in self.attributes: array_table[attribute].load(self, vertex_array) vertex_array, element_map = numpy.unique(vertex_array, return_inverse=True) element_array = gl_create_element_array(self, element_map, self.gl_element_count) glBufferData(GL_ARRAY_BUFFER, vertex_array.nbytes, vertex_array, GL_STATIC_DRAW) glBufferData(GL_ELEMENT_ARRAY_BUFFER, element_array.nbytes, element_array, GL_STATIC_DRAW) def gl_bind(self): glBindVertexArray(self.gl_vertex_array) def gl_draw(self): glDrawElements(GL_TRIANGLES, self.gl_element_count, GL_UNSIGNED_SHORT, None)
class TevMode(WrapperModel): a = _attribute() b = _attribute() c = _attribute() d = _attribute() function = _attribute() bias = _attribute() scale = _attribute() clamp = _attribute() output = _attribute()
class LightingMode(WrapperModel): material_source = _attribute() ambient_source = _attribute() diffuse_function = _attribute() attenuation_function = _attribute() light_enable = _attribute() use_light = _attribute(UseLightProxy, source_path=+_p)
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()
class BlendMode(WrapperModel): function = _attribute() source_factor = _attribute() destination_factor = _attribute() logical_operation = _attribute()
class DepthMode(WrapperModel): enable = _attribute() function = _attribute() update_enable = _attribute()
class AlphaTest(WrapperModel): function0 = _attribute() reference0 = _attribute() function1 = _attribute() reference1 = _attribute() operator = _attribute()
class ShapeNode(SceneGraphNode): node_type = _attribute() index = _attribute() children = _attribute(_list(SceneGraphNode.create_node))
class TextureMatrix(WrapperModel): shape = _attribute() matrix_type = _attribute() center_s = _attribute() center_t = _attribute() unknown0 = _attribute() scale_s = _attribute() scale_t = _attribute() rotation = _attribute() translation_s = _attribute() translation_t = _attribute() projection_matrix = _attribute() def create_matrix(self): return self.wrapped_object.create_matrix()
class SwapTable(WrapperModel): r = _attribute() g = _attribute() b = _attribute() a = _attribute()
class TevStage(WrapperModel): texcoord = _attribute() texture = _attribute() color = _attribute() color_mode = _attribute(TevMode) alpha_mode = _attribute(TevMode) constant_color = _attribute() constant_alpha = _attribute() color_swap_table = _attribute() texture_swap_table = _attribute() indirect_stage = _attribute() indirect_format = _attribute() indirect_bias_components = _attribute() indirect_matrix = _attribute() wrap_s = _attribute() wrap_t = _attribute() add_previous_texcoord = _attribute() use_original_lod = _attribute() bump_alpha = _attribute() unknown0 = _attribute() unknown1 = _attribute()
class Texture(WrapperModel): name = _attribute() wrap_s = SamplerInvalidatingAttribute() wrap_t = SamplerInvalidatingAttribute() minification_filter = SamplerInvalidatingAttribute() magnification_filter = SamplerInvalidatingAttribute() minimum_lod = SamplerInvalidatingAttribute() maximum_lod = SamplerInvalidatingAttribute() lod_bias = SamplerInvalidatingAttribute() unknown0 = _attribute() unknown1 = _attribute() unknown2 = _attribute() palette = _attribute() images = _attribute() @property def width(self): return self.images[0].width @property def height(self): return self.images[0].height @property def image_format(self): return self.images[0].image_format @property def palette_format(self): return self.palette.palette_format @property def gl_wrap_s(self): if self.wrap_s == gx.CLAMP: return GL_CLAMP_TO_EDGE if self.wrap_s == gx.REPEAT: return GL_REPEAT if self.wrap_s == gx.MIRROR: return GL_MIRRORED_REPEAT raise ValueError('Invalid wrap: {}'.format(self.wrap_s)) @property def gl_wrap_t(self): if self.wrap_t == gx.CLAMP: return GL_CLAMP_TO_EDGE if self.wrap_t == gx.REPEAT: return GL_REPEAT if self.wrap_t == gx.MIRROR: return GL_MIRRORED_REPEAT raise ValueError('Invalid wrap: {}'.format(self.wrap_t)) @property def gl_minification_filter(self): if self.minification_filter == gx.NEAR: return GL_LINEAR if self.minification_filter == gx.LINEAR: return GL_LINEAR if self.minification_filter == gx.NEAR_MIP_NEAR: return GL_NEAREST_MIPMAP_NEAREST if self.minification_filter == gx.LIN_MIP_NEAR: return GL_LINEAR_MIPMAP_NEAREST if self.minification_filter == gx.NEAR_MIP_LIN: return GL_NEAREST_MIPMAP_LINEAR if self.minification_filter == gx.LIN_MIP_LIN: return GL_LINEAR_MIPMAP_LINEAR raise ValueError('Invalid minification filter: {}'.format( self.minification_filter)) @property def gl_magnification_filter(self): if self.magnification_filter == gx.NEAR: return GL_LINEAR if self.magnification_filter == gx.LINEAR: return GL_LINEAR raise ValueError('Invalid magnification filter: {}'.format( self.magnification_filter)) @LazyProperty def _gl_sampler(self): return self.gl_create_resource(gl.Sampler) @LazyProperty def gl_sampler(self): glSamplerParameteri(self._gl_sampler, GL_TEXTURE_WRAP_S, self.gl_wrap_s) glSamplerParameteri(self._gl_sampler, GL_TEXTURE_WRAP_T, self.gl_wrap_t) glSamplerParameteri(self._gl_sampler, GL_TEXTURE_MIN_FILTER, self.gl_minification_filter) glSamplerParameteri(self._gl_sampler, GL_TEXTURE_MAG_FILTER, self.gl_magnification_filter) glSamplerParameterf(self._gl_sampler, GL_TEXTURE_MIN_LOD, self.minimum_lod) glSamplerParameterf(self._gl_sampler, GL_TEXTURE_MAX_LOD, self.maximum_lod) glSamplerParameterf(self._gl_sampler, GL_TEXTURE_LOD_BIAS, self.lod_bias) return self._gl_sampler def gl_sampler_invalidate(self): try: del self.gl_sampler except AttributeError: pass @LazyProperty def _gl_texture(self): return self.gl_create_resource(gl.Texture) @LazyProperty def gl_texture(self): if self.image_format in {gx.TF_I4, gx.TF_I8}: image_format = GL_UNSIGNED_BYTE component_count = GL_RED swizzle = [GL_RED, GL_RED, GL_RED, GL_RED] convert = lambda image: image.decode_to_i8() elif self.image_format in {gx.TF_IA4, gx.TF_IA8}: image_format = GL_UNSIGNED_BYTE component_count = GL_RG swizzle = [GL_RED, GL_RED, GL_RED, GL_GREEN] convert = lambda image: image.decode_to_ia8() elif self.image_format == gx.TF_RGB565: image_format = GL_UNSIGNED_SHORT_5_6_5 component_count = GL_RGB swizzle = [GL_RED, GL_GREEN, GL_BLUE, GL_ONE] convert = lambda image: image.decode_to_rgb565() elif self.image_format in {gx.TF_RGB5A3, gx.TF_RGBA8, gx.TF_CMPR}: image_format = GL_UNSIGNED_BYTE component_count = GL_RGBA swizzle = [GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA] convert = lambda image: image.decode_to_rgba8() elif self.image_format in {gx.TF_CI4, gx.TF_CI8, gx.TF_CI14}: if self.palette_format == gx.TL_IA8: image_format = GL_UNSIGNED_BYTE component_count = GL_RG swizzle = [GL_RED, GL_RED, GL_RED, GL_GREEN] palette = self.palette.decode_to_ia8() elif self.palette_format == gx.TL_RGB565: image_format = GL_UNSIGNED_SHORT_5_6_5 component_count = GL_RGB swizzle = [GL_RED, GL_GREEN, GL_BLUE, GL_ONE] palette = self.palette.decode_to_rgb565() elif self.palette_format == gx.TL_RGB5A3: image_format = GL_UNSIGNED_BYTE component_count = GL_RGBA swizzle = [GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA] palette = self.palette.decode_to_rgba8() else: raise ValueError('Invalid palette format: {}'.format( self.palette_format)) convert = lambda image: image.decode_to_direct_color(palette) else: raise ValueError('Invalid image format: {}'.format( self.image_format)) glBindTexture(GL_TEXTURE_2D, self._gl_texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, len(self.images) - 1) glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, numpy.array(swizzle, numpy.int32)) for level, image in enumerate(self.images): glTexImage2D(GL_TEXTURE_2D, level, component_count, image.width, image.height, 0, component_count, image_format, convert(image)) return self._gl_texture def gl_texture_invalidate(self): try: del self.gl_texture except AttributeError: pass def gl_bind(self, texture_unit): glBindSampler(texture_unit, self.gl_sampler) glActiveTexture(GL_TEXTURE0 + texture_unit) glBindTexture(GL_TEXTURE_2D, self.gl_texture) def gl_delete(self): super().gl_delete() try: del self.gl_sampler except AttributeError: pass try: del self._gl_sampler except AttributeError: pass try: del self.gl_texture except AttributeError: pass try: del self._gl_texture except AttributeError: pass @staticmethod def load(file_path): with open(file_path, 'rb') as stream: texture = gx.bti.unpack(stream) texture.name = os.path.splitext(os.path.basename(file_path))[0] return Texture(texture) def save(self, file_path): with open(file_path, 'wb') as stream: gx.bti.pack(stream, self.viewed_object)
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
class Channel(WrapperModel): color_mode = _attribute(LightingMode) alpha_mode = _attribute(LightingMode) material_color = _attribute() ambient_color = _attribute()
class TexCoordGenerator(WrapperModel): function = _attribute() source = _attribute() matrix = _attribute()
class IndirectStage(WrapperModel): texcoord = _attribute() texture = _attribute() scale_s = _attribute() scale_t = _attribute()
class IndirectMatrix(WrapperModel): significand_matrix = _attribute() scale_exponent = _attribute()
class MaterialNode(SceneGraphNode): node_type = _attribute() material = ReferenceAttribute() children = _attribute(_list(SceneGraphNode.create_node))