mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-09-18 18:00:05 +02:00
258 lines
8.2 KiB
Python
258 lines
8.2 KiB
Python
|
import os
|
||
|
from .. import constants, logger
|
||
|
from . import (
|
||
|
base_classes,
|
||
|
texture,
|
||
|
material,
|
||
|
geometry,
|
||
|
object as object_,
|
||
|
utilities,
|
||
|
io,
|
||
|
api
|
||
|
)
|
||
|
|
||
|
|
||
|
class Scene(base_classes.BaseScene):
|
||
|
"""Class that handles the contruction of a Three scene"""
|
||
|
_defaults = {
|
||
|
constants.METADATA: constants.DEFAULT_METADATA.copy(),
|
||
|
constants.GEOMETRIES: [],
|
||
|
constants.MATERIALS: [],
|
||
|
constants.IMAGES: [],
|
||
|
constants.TEXTURES: []
|
||
|
}
|
||
|
|
||
|
def __init__(self, filepath, options=None):
|
||
|
logger.debug("Scene().__init__(%s, %s)", filepath, options)
|
||
|
base_classes.BaseScene.__init__(self, filepath, options or {})
|
||
|
|
||
|
source_file = api.scene_name()
|
||
|
if source_file:
|
||
|
self[constants.METADATA][constants.SOURCE_FILE] = source_file
|
||
|
|
||
|
@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)
|
||
|
|