diff options
Diffstat (limited to 'io_convert_image_to_mesh_img/ui/importer.py')
-rw-r--r-- | io_convert_image_to_mesh_img/ui/importer.py | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/io_convert_image_to_mesh_img/ui/importer.py b/io_convert_image_to_mesh_img/ui/importer.py new file mode 100644 index 00000000..981e49c2 --- /dev/null +++ b/io_convert_image_to_mesh_img/ui/importer.py @@ -0,0 +1,177 @@ +# This file is a part of the HiRISE DTM Importer for Blender +# +# Copyright (C) 2017 Arizona Board of Regents on behalf of the Planetary Image +# Research Laboratory, Lunar and Planetary Laboratory at the University of +# Arizona. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +"""Blender menu importer for loading a DTM""" + +import bpy +import bpy.props +from bpy_extras.io_utils import ImportHelper + +from ..mesh.terrain import BTerrain +from ..mesh.dtm import DTM + + +class ImportHiRISETerrain(bpy.types.Operator, ImportHelper): + """DTM Import Helper""" + bl_idname = "import.pds_dtm" + bl_label = "Import HiRISE Terrain Model" + bl_options = {'UNDO'} + filename_ext = ".img" + filter_glob = bpy.props.StringProperty( + options={'HIDDEN'}, + default="*.img" + ) + + # Allow the user to specify a resolution factor for loading the + # terrain data at. This is useful because it allows the user to stage + # a scene with a low resolution terrain map, apply textures, modifiers, + # etc. and then increase the resolution to prepare for final rendering. + # + # Displaying this value as a percentage (0, 100] is an intuitive way + # for users to grasp what this value does. The DTM importer, however, + # wants to recieve a value between (0, 1]. This is obviously a + # straightforward conversion: + # + # f(x) = x / 100 + # + # But this conversion should happen here, in the terrain panel, rather + # than in the DTM importing utility itself. We can't pass get/set + # functions to the property itself because they result in a recursion + # error. Instead, we use another, hidden, property to store the scaled + # resolution. + dtm_resolution = bpy.props.FloatProperty( + subtype="PERCENTAGE", + description=( + "Percentage scale for terrain model resolution. 100\% loads the " + "model at full resolution (i.e. one vertex for each post in the " + "original terrain model) and is *MEMORY INTENSIVE*. Downsampling " + "uses Nearest Neighbors. You will be able to increase the " + "resolution of your mesh later, and still maintain all textures, " + "transformations, modifiers, etc., so best practice is to start " + "small. The downsampling algorithm may need to alter the " + "resolution you specify here to ensure it results in a whole " + "number of vertices. If it needs to alter the value you specify, " + "you are guaranteed that it will shrink it (i.e. decrease the " + "DTM resolution." + ), + name="Terrain Model Resolution", + min=1.0, max=100.0, default=10.0 + ) + scaled_dtm_resolution = bpy.props.FloatProperty( + options={'HIDDEN'}, + name="Scaled Terrain Model Resolution", + get=(lambda self: self.dtm_resolution / 100) + ) + + # HiRISE DTMs are huge, but it can be nice to load them in at scale. Here, + # we present the user with the option of setting up the Blender viewport + # to avoid a couple of common pitfalls encountered when working with such + # a large mesh. + # + # 1. The Blender viewport has a default clipping distance of 1km. HiRISE + # DTMs are often many kilometers in each direction. If this setting is + # not changed, an unsuspecting user may only see part (or even nothing + # at all) of the terrain. This option (true, by default) instructs + # Blender to change the clipping distance to something appropriate for + # the DTM, and scales the grid floor to have gridlines 1km apart, + # instead of 1m apart. + should_setup_viewport = bpy.props.BoolProperty( + description=( + "Set up the Blender screen to try and avoid clipping the DTM " + "and to make the grid floor larger. *WARNING* This will change " + "clipping distances and the Blender grid floor, and will fit the " + "DTM in the viewport." + ), + name="Setup Blender Scene", default=True + ) + # 2. Blender's default units are dimensionless. This option instructs + # Blender to change its unit's dimension to meters. + should_setup_units = bpy.props.BoolProperty( + description=( + "Set the Blender scene to use meters as its unit." + ), + name="Set Blender Units to Meters", default=True + ) + + def execute(self, context): + """Runs when the "Import HiRISE Terrain Model" button is pressed""" + filepath = bpy.path.ensure_ext(self.filepath, self.filename_ext) + # Create a BTerrain from the DTM + dtm = DTM(filepath, self.scaled_dtm_resolution) + BTerrain.new(dtm) + + # Set up the Blender UI + if self.should_setup_units: + self._setup_units(context) + if self.should_setup_viewport: + self._setup_viewport(context) + + return {"FINISHED"} + + def _setup_units(self, context): + """Sets up the Blender scene for viewing the DTM""" + scene = bpy.context.scene + + # Set correct units + scene.unit_settings.system = 'METRIC' + scene.unit_settings.scale_length = 1.0 + + return {'FINISHED'} + + def _setup_viewport(self, context): + """Sets up the Blender screen to make viewing the DTM easier""" + screen = bpy.context.screen + + # Fetch the 3D_VIEW Area + for area in screen.areas: + if area.type == 'VIEW_3D': + space = area.spaces[0] + # Adjust 3D View Properties + # TODO: Can these be populated more intelligently? + space.clip_end = 100000 + space.grid_scale = 1000 + space.grid_lines = 50 + + # Fly to a nice view of the DTM + self._view_dtm(context) + + return {'FINISHED'} + + def _view_dtm(self, context): + """Sets up the Blender screen to make viewing the DTM easier""" + screen = bpy.context.screen + + # Fetch the 3D_VIEW Area + for area in screen.areas: + if area.type == 'VIEW_3D': + # Move the camera around in the viewport. This requires + # a context override. + for region in area.regions: + if region.type == 'WINDOW': + override = { + 'area': area, + 'region': region, + 'edit_object': bpy.context.edit_object + } + # Center View on DTM (SHORTCUT: '.') + bpy.ops.view3d.view_selected(override) + # Move to 'TOP' viewport (SHORTCUT: NUMPAD7) + bpy.ops.view3d.viewnumpad(override, type='TOP') + + return {'FINISHED'} |