""" 3do model parser. Author: Cyril Zorin Creation date: 28 september 2006 """ from hachoir.parser import Parser from hachoir.field import (FieldSet, UInt32, Int32, String, Float32, RawBytes, PaddingBytes) from hachoir.core.endian import LITTLE_ENDIAN, BIG_ENDIAN from hachoir.parser.misc.common import Vertex, MapUV class Vector(FieldSet): def __init__(self, parent, name, count, type, ename, edesc=None, description=None): FieldSet.__init__(self, parent, name, description) self.count = count self.type = type self.ename = ename + "[]" self.edesc = edesc try: item_size = self.type.static_size(self.ename, self.edesc) except TypeError: item_size = self.type.static_size if item_size: self._size = item_size * self.count def createFields(self): for index in range(self.count): yield self.type(self, self.ename, self.edesc) class Face(FieldSet): def createFields(self): yield UInt32(self, "id") yield UInt32(self, "type") yield UInt32(self, "geometry_mode") yield UInt32(self, "lighting_mode") yield UInt32(self, "texture_mode") yield UInt32(self, "nvertices") yield Float32(self, "unknown[]", "unknown") yield UInt32(self, "has_texture", "Has texture?") yield UInt32(self, "has_material", "Has material?") yield Vertex(self, "unknown[]") yield Float32(self, "extra_light") yield Vertex(self, "unknown[]") yield Vertex(self, "normal") if self["nvertices"].value: yield Vector(self, "vertex_indices", self["nvertices"].value, UInt32, "vertex") if self["has_texture"].value: yield Vector(self, "texture_vertex_indices", self["nvertices"].value, UInt32, "texture_vertex") if self["has_material"].value: yield UInt32(self, "material_index", "material index") def createDescription(self): return "Face: id=%s" % self["id"].value class Mesh(FieldSet): def __init__(self, *args): FieldSet.__init__(self, *args) def createFields(self): yield String(self, "name", 32, strip="\0") yield UInt32(self, "id") yield UInt32(self, "geometry_mode") yield UInt32(self, "lighting_mode") yield UInt32(self, "texture_mode") yield UInt32(self, "nmesh_vertices") yield UInt32(self, "ntexture_vertices") yield UInt32(self, "nfaces") nb_vert = self["nmesh_vertices"].value if nb_vert: yield Vector(self, "vertices", nb_vert, Vertex, "vertex") if self["ntexture_vertices"].value: yield Vector(self, "texture vertices", self["ntexture_vertices"].value, MapUV, "texture_vertex") if nb_vert: yield Vector(self, "light vertices", nb_vert, Float32, "extra_light") yield Vector(self, "unknown[]", nb_vert, Float32, "unknown") if self["nfaces"].value: yield Vector(self, "faces", self["nfaces"].value, Face, "face") if nb_vert: yield Vector(self, "vertex normals", nb_vert, Vertex, "normal") yield UInt32(self, "has_shadow") yield Float32(self, "unknown[]") yield Float32(self, "radius") yield Vertex(self, "unknown[]") yield Vertex(self, "unknown[]") def createDescription(self): return 'Mesh "%s" (id %s)' % (self["name"].value, self["id"].value) class Geoset(FieldSet): def createFields(self): yield UInt32(self, "count") for index in range(self["count"].value): yield Mesh(self, "mesh[]") def createDescription(self): return "Set of %s meshes" % self["count"].value class Node(FieldSet): def __init__(self, *args): FieldSet.__init__(self, *args) size = (188 - 4) * 8 if self["parent_offset"].value != 0: size += 32 if self["first_child_offset"].value != 0: size += 32 if self["next_sibling_offset"].value != 0: size += 32 self._size = size def createFields(self): yield String(self, "name", 32, strip="\0") yield PaddingBytes(self, "unknown[]", 32, pattern="\xCC") yield UInt32(self, "flags") yield UInt32(self, "id") yield UInt32(self, "type") yield Int32(self, "mesh_id") yield UInt32(self, "depth") yield Int32(self, "parent_offset") yield UInt32(self, "nchildren") yield UInt32(self, "first_child_offset") yield UInt32(self, "next_sibling_offset") yield Vertex(self, "pivot") yield Vertex(self, "position") yield Float32(self, "pitch") yield Float32(self, "yaw") yield Float32(self, "roll") for index in range(4): yield Vertex(self, "unknown_vertex[]") if self["parent_offset"].value != 0: yield UInt32(self, "parent_id") if self["first_child_offset"].value != 0: yield UInt32(self, "first_child_id") if self["next_sibling_offset"].value != 0: yield UInt32(self, "next_sibling_id") def createDescription(self): return 'Node "%s"' % self["name"].value class Nodes(FieldSet): def createFields(self): yield UInt32(self, "count") for index in range(self["count"].value): yield Node(self, "node[]") def createDescription(self): return 'Nodes (%s)' % self["count"].value class Materials(FieldSet): def __init__(self, *args): FieldSet.__init__(self, *args) count = self["count"] self._size = count.size + count.value * (32 * 8) def createFields(self): yield UInt32(self, "count") for index in range(self["count"].value): yield String(self, "filename[]", 32, "Material file name", strip="\0") def createDescription(self): return 'Material file names (%s)' % self["count"].value class File3do(Parser): PARSER_TAGS = { "id": "3do", "category": "misc", "file_ext": ("3do",), "mime": ("image/x-3do",), "min_size": 8 * 4, "description": "renderdroid 3d model." } endian = LITTLE_ENDIAN def validate(self): signature = self.stream.readBytes(0, 4) return signature in (b'LDOM', b'MODL') # lazy endian-safe hack =D def createFields(self): # Read file signature, and fix endian if needed yield String(self, "file_sig", 4, "File signature", charset="ASCII") if self["file_sig"].value == "MODL": self.endian = BIG_ENDIAN # Read file content yield Materials(self, "materials") yield String(self, "model_name", 32, "model file name", strip="\0") yield RawBytes(self, "unknown[]", 4) yield UInt32(self, "ngeosets") for index in range(self["ngeosets"].value): yield Geoset(self, "geoset[]") yield RawBytes(self, "unknown[]", 4) yield Nodes(self, "nodes") yield Float32(self, "model_radius") yield Vertex(self, "insertion_offset") # Read the end of the file if self.current_size < self._size: yield self.seekBit(self._size, "end")