Spaces:
Running
Running
| # -*- coding: utf-8 -*- | |
| """Import LDraw GPLv2 license. | |
| This program is free software; you can redistribute it and/or | |
| modify it under the terms of the GNU General Public License | |
| as published by the Free Software Foundation; either version 2 | |
| of the License, or (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software Foundation, | |
| Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| """ | |
| """ | |
| Import LDraw | |
| This file defines the importer for Blender. | |
| It stores and recalls preferences for the importer. | |
| The execute() function kicks off the import process. | |
| The python module loadldraw does the actual work. | |
| """ | |
| import configparser | |
| import os | |
| import bpy | |
| from bpy.props import (StringProperty, | |
| FloatProperty, | |
| EnumProperty, | |
| BoolProperty | |
| ) | |
| from bpy_extras.io_utils import ImportHelper | |
| from .loadldraw import loadldraw | |
| """ | |
| Example preferences file: | |
| [DEFAULT] | |
| [importldraw] | |
| ldrawDirectory = "" | |
| realScale = 0.0002 | |
| resolution = "Standard" | |
| smoothShading = True | |
| useLook = "normal" | |
| useColourScheme = "lgeo" | |
| gaps = True | |
| realGapWidth = 0.0002 | |
| createInstances = True | |
| numberNodes = True | |
| positionObjectOnGroundAtOrigin = True | |
| flattenHierarchy = False | |
| useUnofficialParts = True | |
| useLogoStuds = False | |
| instanceStuds = False | |
| curvedWalls = True | |
| (etc) | |
| """ | |
| class Preferences(): | |
| """Import LDraw - Preferences""" | |
| __sectionName = 'importldraw' | |
| def __init__(self): | |
| self.__ldPath = None | |
| self.__prefsPath = os.path.dirname(__file__) | |
| self.__prefsFilepath = os.path.join(self.__prefsPath, "ImportLDrawPreferences.ini") | |
| self.__config = configparser.RawConfigParser() | |
| self.__prefsRead = self.__config.read(self.__prefsFilepath) | |
| if self.__prefsRead and not self.__config[Preferences.__sectionName]: | |
| self.__prefsRead = False | |
| def get(self, option, default): | |
| if not self.__prefsRead: | |
| return default | |
| if type(default) is bool: | |
| return self.__config.getboolean(Preferences.__sectionName, option, fallback=default) | |
| elif type(default) is float: | |
| return self.__config.getfloat(Preferences.__sectionName, option, fallback=default) | |
| elif type(default) is int: | |
| return self.__config.getint(Preferences.__sectionName, option, fallback=default) | |
| else: | |
| return self.__config.get(Preferences.__sectionName, option, fallback=default) | |
| def set(self, option, value): | |
| if not (Preferences.__sectionName in self.__config): | |
| self.__config[Preferences.__sectionName] = {} | |
| self.__config[Preferences.__sectionName][option] = str(value) | |
| def save(self): | |
| try: | |
| with open(self.__prefsFilepath, 'w') as configfile: | |
| self.__config.write(configfile) | |
| return True | |
| except Exception: | |
| # Fail gracefully | |
| e = sys.exc_info()[0] | |
| debugPrint("WARNING: Could not save preferences. {0}".format(e)) | |
| return False | |
| class ImportLDrawOps(bpy.types.Operator, ImportHelper): | |
| """Import LDraw - Import Operator.""" | |
| bl_idname = "import_scene.importldraw" | |
| bl_description = "Import LDraw models (.io/.mpd/.ldr/.l3b/.dat)" | |
| bl_label = "Import LDraw Models" | |
| bl_space_type = "PROPERTIES" | |
| bl_region_type = "WINDOW" | |
| bl_options = {'REGISTER', 'UNDO', 'PRESET'} | |
| # Instance the preferences system | |
| prefs = Preferences() | |
| # File type filter in file browser | |
| filename_ext = ".ldr" | |
| filter_glob: StringProperty( | |
| default="*.io;*.mpd;*.ldr;*.l3b;*.dat", | |
| options={'HIDDEN'} | |
| ) | |
| ldrawPath: StringProperty( | |
| name="", | |
| description="Full filepath to the LDraw Parts Library (download from http://www.ldraw.org)", | |
| default=prefs.get("ldrawDirectory", loadldraw.Configure.findDefaultLDrawDirectory()) | |
| ) | |
| realScale: FloatProperty( | |
| name="Scale", | |
| description="Sets a scale for the model (1.0 = real life scale)", | |
| default=prefs.get("realScale", 1.0) | |
| ) | |
| resPrims: EnumProperty( | |
| name="Resolution of part primitives", | |
| description="Resolution of part primitives, ie. how much geometry they have", | |
| default=prefs.get("resolution", "Standard"), | |
| items=( | |
| ("Standard", "Standard primitives", "Import using standard resolution primitives."), | |
| ("High", "High resolution primitives", "Import using high resolution primitives."), | |
| ("Low", "Low resolution primitives", "Import using low resolution primitives.") | |
| ) | |
| ) | |
| smoothParts: BoolProperty( | |
| name="Smooth faces and edge-split", | |
| description="Smooth faces and add an edge-split modifier", | |
| default=prefs.get("smoothShading", True) | |
| ) | |
| look: EnumProperty( | |
| name="Overall Look", | |
| description="Realism or Schematic look", | |
| default=prefs.get("useLook", "normal"), | |
| items=( | |
| ("normal", "Realistic Look", "Render to look realistic."), | |
| ("instructions", "Lego Instructions Look", "Render to look like the instruction book pictures."), | |
| ) | |
| ) | |
| colourScheme: EnumProperty( | |
| name="Colour scheme options", | |
| description="Colour scheme options", | |
| default=prefs.get("useColurScheme", "lgeo"), | |
| items=( | |
| ("lgeo", "Realistic colours", "Uses the LGEO colour scheme for realistic colours."), | |
| ("ldraw", "Original LDraw colours", "Uses the standard LDraw colour scheme. Looks good with the Instructions Look."), | |
| ("alt", "Alternate LDraw colours", "Uses the alternate LDraw colour scheme. Looks good with the Instructions Look."), | |
| ) | |
| ) | |
| addGaps: BoolProperty( | |
| name="Add space between each part:", | |
| description="Add a small space between each part", | |
| default=prefs.get("gaps", False) | |
| ) | |
| gapWidthMM: FloatProperty( | |
| name="Space", | |
| description="Amount of space between each part (default 0.2mm)", | |
| default=1000 * prefs.get("realGapWidth", 0.0002) | |
| ) | |
| curvedWalls: BoolProperty( | |
| name="Use curved wall normals", | |
| description="Makes surfaces look slightly concave, for interesting reflections", | |
| default=prefs.get("curvedWalls", True) | |
| ) | |
| importCameras: BoolProperty( | |
| name="Import cameras", | |
| description="Import camera definitions (from models authored in LeoCAD)", | |
| default=prefs.get("importCameras", True) | |
| ) | |
| linkParts: BoolProperty( | |
| name="Link identical parts", | |
| description="Identical parts (of the same type and colour) share the same mesh", | |
| default=prefs.get("linkParts", True) | |
| ) | |
| numberNodes: BoolProperty( | |
| name="Number each object", | |
| description="Each object has a five digit prefix eg. 00001_car. This keeps the list in it's proper order", | |
| default=prefs.get("numberNodes", True) | |
| ) | |
| positionOnGround: BoolProperty( | |
| name="Put model on ground at origin", | |
| description="The object is centred at the origin, and on the ground plane", | |
| default=prefs.get("positionObjectOnGroundAtOrigin", True) | |
| ) | |
| flatten: BoolProperty( | |
| name="Flatten tree", | |
| description="In Scene Outliner, all parts are placed directly below the root - there's no tree of submodels", | |
| default=prefs.get("flattenHierarchy", False) | |
| ) | |
| minifigHierarchy: BoolProperty( | |
| name="Parent Minifigs", | |
| description="Add a parent/child hierarchy (tree) for Minifigs", | |
| default=prefs.get("minifigHierarchy", True) | |
| ) | |
| useUnofficialParts: BoolProperty( | |
| name="Include unofficial parts", | |
| description="Additionally searches for parts in the <ldraw-dir>/unofficial/ directory", | |
| default=prefs.get("useUnofficialParts", True) | |
| ) | |
| useLogoStuds: BoolProperty( | |
| name="Show 'LEGO' logo on studs", | |
| description="Shows the LEGO logo on each stud (at the expense of some extra geometry and import time)", | |
| default=prefs.get("useLogoStuds", False) | |
| ) | |
| instanceStuds: BoolProperty( | |
| name="Make individual studs", | |
| description="Creates a Blender Object for each and every stud (WARNING: can be slow to import and edit in Blender if there are lots of studs)", | |
| default=prefs.get("instanceStuds", False) | |
| ) | |
| resolveNormals: EnumProperty( | |
| name="Resolve ambiguous normals option", | |
| description="Some older LDraw parts have faces with ambiguous normals, this specifies what do do with them", | |
| default=prefs.get("resolveNormals", "guess"), | |
| items=( | |
| ("guess", "Recalculate Normals", "Uses Blender's Recalculate Normals to get a consistent set of normals."), | |
| ("double", "Two faces back to back", "Two faces are added with their normals pointing in opposite directions."), | |
| ) | |
| ) | |
| bevelEdges: BoolProperty( | |
| name="Bevel edges", | |
| description="Adds a Bevel modifier for rounding off sharp edges", | |
| default=prefs.get("bevelEdges", True) | |
| ) | |
| bevelWidth: FloatProperty( | |
| name="Bevel Width", | |
| description="Width of the bevelled edges", | |
| default=prefs.get("bevelWidth", 0.5) | |
| ) | |
| addEnvironment: BoolProperty( | |
| name="Add Environment", | |
| description="Adds a ground plane and environment texture (for realistic look only)", | |
| default=prefs.get("addEnvironment", True) | |
| ) | |
| positionCamera: BoolProperty( | |
| name="Position the camera", | |
| description="Position the camera to show the whole model", | |
| default=prefs.get("positionCamera", True) | |
| ) | |
| cameraBorderPercentage: FloatProperty( | |
| name="Camera Border %", | |
| description="When positioning the camera, include a (percentage) border leeway around the model in the rendered image", | |
| default=prefs.get("cameraBorderPercentage", 5.0) | |
| ) | |
| def draw(self, context): | |
| """Display import options.""" | |
| layout = self.layout | |
| layout.use_property_split = True # Active single-column layout | |
| box = layout.box() | |
| box.label(text="Import Options", icon='PREFERENCES') | |
| box.label(text="LDraw filepath:", icon='FILEBROWSER') | |
| box.prop(self, "ldrawPath") | |
| box.prop(self, "realScale") | |
| box.prop(self, "look", expand=True) | |
| box.prop(self, "addEnvironment") | |
| box.prop(self, "positionCamera") | |
| box.prop(self, "cameraBorderPercentage") | |
| box.prop(self, "colourScheme", expand=True) | |
| box.prop(self, "resPrims", expand=True) | |
| box.prop(self, "smoothParts") | |
| box.prop(self, "bevelEdges") | |
| box.prop(self, "bevelWidth") | |
| box.prop(self, "addGaps") | |
| box.prop(self, "gapWidthMM") | |
| box.prop(self, "curvedWalls") | |
| box.prop(self, "importCameras") | |
| box.prop(self, "linkParts") | |
| box.prop(self, "useUnofficialParts") | |
| box.prop(self, "useLogoStuds") | |
| box.prop(self, "instanceStuds") | |
| box.prop(self, "positionOnGround") | |
| box.prop(self, "numberNodes") | |
| box.prop(self, "flatten") | |
| box.prop(self, "minifigHierarchy") | |
| box.label(text="Resolve Ambiguous Normals:", icon='ORIENTATION_NORMAL') | |
| box.prop(self, "resolveNormals", expand=True) | |
| def execute(self, context): | |
| """Start the import process.""" | |
| # Read current preferences from the UI and save them | |
| ImportLDrawOps.prefs.set("ldrawDirectory", self.ldrawPath) | |
| ImportLDrawOps.prefs.set("realScale", self.realScale) | |
| ImportLDrawOps.prefs.set("resolution", self.resPrims) | |
| ImportLDrawOps.prefs.set("smoothShading", self.smoothParts) | |
| ImportLDrawOps.prefs.set("bevelEdges", self.bevelEdges) | |
| ImportLDrawOps.prefs.set("bevelWidth", self.bevelWidth) | |
| ImportLDrawOps.prefs.set("useLook", self.look) | |
| ImportLDrawOps.prefs.set("useColourScheme", self.colourScheme) | |
| ImportLDrawOps.prefs.set("gaps", self.addGaps) | |
| ImportLDrawOps.prefs.set("realGapWidth", self.gapWidthMM / 1000) | |
| ImportLDrawOps.prefs.set("curvedWalls", self.curvedWalls) | |
| ImportLDrawOps.prefs.set("importCameras", self.importCameras) | |
| ImportLDrawOps.prefs.set("linkParts", self.linkParts) | |
| ImportLDrawOps.prefs.set("numberNodes", self.numberNodes) | |
| ImportLDrawOps.prefs.set("positionObjectOnGroundAtOrigin", self.positionOnGround) | |
| ImportLDrawOps.prefs.set("flattenHierarchy", self.flatten) | |
| ImportLDrawOps.prefs.set("minifigHierarchy", self.minifigHierarchy) | |
| ImportLDrawOps.prefs.set("useUnofficialParts", self.useUnofficialParts) | |
| ImportLDrawOps.prefs.set("useLogoStuds", self.useLogoStuds) | |
| ImportLDrawOps.prefs.set("instanceStuds", self.instanceStuds) | |
| ImportLDrawOps.prefs.set("resolveNormals", self.resolveNormals) | |
| ImportLDrawOps.prefs.set("addEnvironment", self.addEnvironment) | |
| ImportLDrawOps.prefs.set("positionCamera", self.positionCamera) | |
| ImportLDrawOps.prefs.set("cameraBorderPercentage",self.cameraBorderPercentage) | |
| ImportLDrawOps.prefs.save() | |
| # Set bpy related variables here since it isn't available immediately on Blender startup | |
| loadldraw.hasCollections = hasattr(bpy.data, "collections") | |
| # Set import options and import | |
| loadldraw.Options.ldrawDirectory = self.ldrawPath | |
| loadldraw.Options.realScale = self.realScale | |
| loadldraw.Options.useUnofficialParts = self.useUnofficialParts | |
| loadldraw.Options.resolution = self.resPrims | |
| loadldraw.Options.defaultColour = "4" | |
| loadldraw.Options.createInstances = self.linkParts | |
| loadldraw.Options.instructionsLook = self.look == "instructions" | |
| loadldraw.Options.useColourScheme = self.colourScheme | |
| loadldraw.Options.numberNodes = self.numberNodes | |
| loadldraw.Options.removeDoubles = True | |
| loadldraw.Options.smoothShading = self.smoothParts | |
| loadldraw.Options.edgeSplit = self.smoothParts # Edge split is appropriate only if we are smoothing | |
| loadldraw.Options.gaps = self.addGaps | |
| loadldraw.Options.realGapWidth = self.gapWidthMM / 1000 | |
| loadldraw.Options.curvedWalls = self.curvedWalls | |
| loadldraw.Options.importCameras = self.importCameras | |
| loadldraw.Options.positionObjectOnGroundAtOrigin = self.positionOnGround | |
| loadldraw.Options.flattenHierarchy = self.flatten | |
| loadldraw.Options.minifigHierarchy = self.minifigHierarchy | |
| loadldraw.Options.useLogoStuds = self.useLogoStuds | |
| loadldraw.Options.logoStudVersion = "4" | |
| loadldraw.Options.instanceStuds = self.instanceStuds | |
| loadldraw.Options.useLSynthParts = True | |
| loadldraw.Options.LSynthDirectory = os.path.join(os.path.dirname(__file__), "lsynth") | |
| loadldraw.Options.studLogoDirectory = os.path.join(os.path.dirname(__file__), "studs") | |
| loadldraw.Options.resolveAmbiguousNormals = self.resolveNormals | |
| loadldraw.Options.overwriteExistingMaterials = False | |
| loadldraw.Options.overwriteExistingMeshes = False | |
| loadldraw.Options.addBevelModifier = self.bevelEdges and not loadldraw.Options.instructionsLook | |
| loadldraw.Options.bevelWidth = self.bevelWidth | |
| loadldraw.Options.addWorldEnvironmentTexture = self.addEnvironment | |
| loadldraw.Options.addGroundPlane = self.addEnvironment | |
| loadldraw.Options.positionCamera = self.positionCamera | |
| loadldraw.Options.cameraBorderPercent = self.cameraBorderPercentage / 100.0 | |
| loadldraw.loadFromFile(self, self.filepath) | |
| return {'FINISHED'} | |