123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- import os
- from .. import constants, logger
- from . import (
- base_classes,
- texture,
- material,
- geometry,
- object as object_,
- utilities,
- io,
- api
- )
- from bpy import context
- class Scene(base_classes.BaseScene):
- """Class that handles the contruction of a Three scene"""
- def __init__(self, filepath, options=None):
- logger.debug("Scene().__init__(%s, %s)", filepath, options)
- self._defaults = {
- constants.METADATA: constants.DEFAULT_METADATA.copy(),
- constants.GEOMETRIES: [],
- constants.MATERIALS: [],
- constants.IMAGES: [],
- constants.TEXTURES: [],
- constants.ANIMATION: []
- }
- base_classes.BaseScene.__init__(self, filepath, options or {})
- source_file = api.scene_name()
- if source_file:
- self[constants.METADATA][constants.SOURCE_FILE] = source_file
- self.__init_animation()
- def __init_animation(self):
- self[constants.ANIMATION].append({
- constants.NAME: "default",
- constants.FPS : context.scene.render.fps,
- constants.KEYFRAMES: []
- });
- pass
- @property
- def valid_types(self):
- """
- :return: list of valid node types
- """
- valid_types = [api.constants.MESH]
- if self.options.get(constants.HIERARCHY, False):
- valid_types.append(api.constants.EMPTY)
- if self.options.get(constants.CAMERAS):
- logger.info("Adding cameras to valid object types")
- valid_types.append(api.constants.CAMERA)
- if self.options.get(constants.LIGHTS):
- logger.info("Adding lights to valid object types")
- valid_types.append(api.constants.LAMP)
- return valid_types
- def geometry(self, value):
- """Find a geometry node that matches either a name
- or uuid value.
- :param value: name or uuid
- :type value: str
- """
- logger.debug("Scene().geometry(%s)", value)
- return _find_node(value, self[constants.GEOMETRIES])
- def image(self, value):
- """Find a image node that matches either a name
- or uuid value.
- :param value: name or uuid
- :type value: str
- """
- logger.debug("Scene().image%s)", value)
- return _find_node(value, self[constants.IMAGES])
- def material(self, value):
- """Find a material node that matches either a name
- or uuid value.
- :param value: name or uuid
- :type value: str
- """
- logger.debug("Scene().material(%s)", value)
- return _find_node(value, self[constants.MATERIALS])
- def parse(self):
- """Execute the parsing of the scene"""
- logger.debug("Scene().parse()")
- if self.options.get(constants.MAPS):
- self._parse_textures()
- if self.options.get(constants.MATERIALS):
- self._parse_materials()
- self._parse_geometries()
- self._parse_objects()
- def texture(self, value):
- """Find a texture node that matches either a name
- or uuid value.
- :param value: name or uuid
- :type value: str
- """
- logger.debug("Scene().texture(%s)", value)
- return _find_node(value, self[constants.TEXTURES])
- def write(self):
- """Write the parsed scene to disk."""
- logger.debug("Scene().write()")
- data = {}
- embed_anim = self.options.get(constants.EMBED_ANIMATION, True)
- embed = self.options.get(constants.EMBED_GEOMETRY, True)
- compression = self.options.get(constants.COMPRESSION)
- extension = constants.EXTENSIONS.get(
- compression,
- constants.EXTENSIONS[constants.JSON])
- export_dir = os.path.dirname(self.filepath)
- for key, value in self.items():
- if key == constants.GEOMETRIES:
- geometries = []
- for geom in value:
- if not embed_anim:
- geom.write_animation(export_dir)
- geom_data = geom.copy()
- if embed:
- geometries.append(geom_data)
- continue
- geo_type = geom_data[constants.TYPE].lower()
- if geo_type == constants.GEOMETRY.lower():
- geom_data.pop(constants.DATA)
- elif geo_type == constants.BUFFER_GEOMETRY.lower():
- geom_data.pop(constants.ATTRIBUTES)
- geom_data.pop(constants.METADATA)
- url = 'geometry.%s%s' % (geom.node, extension)
- geometry_file = os.path.join(export_dir, url)
- geom.write(filepath=geometry_file)
- geom_data[constants.URL] = os.path.basename(url)
- geometries.append(geom_data)
- data[key] = geometries
- elif isinstance(value, list):
- data[key] = []
- for each in value:
- data[key].append(each.copy())
- elif isinstance(value, dict):
- data[key] = value.copy()
- io.dump(self.filepath, data, options=self.options)
- if self.options.get(constants.COPY_TEXTURES):
- texture_folder = self.options.get(constants.TEXTURE_FOLDER)
- for geo in self[constants.GEOMETRIES]:
- logger.info("Copying textures from %s", geo.node)
- geo.copy_textures(texture_folder)
- def _parse_geometries(self):
- """Locate all geometry nodes and parse them"""
- logger.debug("Scene()._parse_geometries()")
- # this is an important step. please refer to the doc string
- # on the function for more information
- api.object.prep_meshes(self.options)
- geometries = []
- # now iterate over all the extracted mesh nodes and parse each one
- for mesh in api.object.extracted_meshes():
- logger.info("Parsing geometry %s", mesh)
- geo = geometry.Geometry(mesh, self)
- geo.parse()
- geometries.append(geo)
- logger.info("Added %d geometry nodes", len(geometries))
- self[constants.GEOMETRIES] = geometries
- def _parse_materials(self):
- """Locate all non-orphaned materials and parse them"""
- logger.debug("Scene()._parse_materials()")
- materials = []
- for material_name in api.material.used_materials():
- logger.info("Parsing material %s", material_name)
- materials.append(material.Material(material_name, parent=self))
- logger.info("Added %d material nodes", len(materials))
- self[constants.MATERIALS] = materials
- def _parse_objects(self):
- """Locate all valid objects in the scene and parse them"""
- logger.debug("Scene()._parse_objects()")
- try:
- scene_name = self[constants.METADATA][constants.SOURCE_FILE]
- except KeyError:
- scene_name = constants.SCENE
- self[constants.OBJECT] = object_.Object(None, parent=self)
- self[constants.OBJECT][constants.TYPE] = constants.SCENE.title()
- self[constants.UUID] = utilities.id_from_name(scene_name)
- objects = []
- if self.options.get(constants.HIERARCHY, False):
- nodes = api.object.assemblies(self.valid_types, self.options)
- else:
- nodes = api.object.nodes(self.valid_types, self.options)
- for node in nodes:
- logger.info("Parsing object %s", node)
- obj = object_.Object(node, parent=self[constants.OBJECT])
- objects.append(obj)
- logger.info("Added %d object nodes", len(objects))
- self[constants.OBJECT][constants.CHILDREN] = objects
- def _parse_textures(self):
- """Locate all non-orphaned textures and parse them"""
- logger.debug("Scene()._parse_textures()")
- textures = []
- for texture_name in api.texture.textures():
- logger.info("Parsing texture %s", texture_name)
- tex_inst = texture.Texture(texture_name, self)
- textures.append(tex_inst)
- logger.info("Added %d texture nodes", len(textures))
- self[constants.TEXTURES] = textures
- def _find_node(value, manifest):
- """Find a node that matches either a name
- or uuid value.
- :param value: name or uuid
- :param manifest: manifest of nodes to search
- :type value: str
- :type manifest: list
- """
- for index in manifest:
- uuid = index.get(constants.UUID) == value
- name = index.node == value
- if uuid or name:
- return index
- else:
- logger.debug("No matching node for %s", value)
|