Fix T58881: FBX error exporting tangent space
[blender-addons.git] / io_convert_image_to_mesh_img / ui / importer.py
blob9ac7371bf54d2d0eb38dfd9d508450c884867107
1 # This file is a part of the HiRISE DTM Importer for Blender
3 # Copyright (C) 2017 Arizona Board of Regents on behalf of the Planetary Image
4 # Research Laboratory, Lunar and Planetary Laboratory at the University of
5 # Arizona.
7 # This program is free software: you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by the Free
9 # Software Foundation, either version 3 of the License, or (at your option)
10 # any later version.
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 # for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Blender menu importer for loading a DTM"""
22 import bpy
23 from bpy.props import (
24 BoolProperty,
25 FloatProperty,
26 StringProperty,
28 from bpy_extras.io_utils import ImportHelper
30 from ..mesh.terrain import BTerrain
31 from ..mesh.dtm import DTM
34 class ImportHiRISETerrain(bpy.types.Operator, ImportHelper):
35 """DTM Import Helper"""
36 bl_idname = "import_mesh.pds_dtm"
37 bl_label = "Import HiRISE Terrain Model"
38 bl_options = {'UNDO'}
40 filename_ext = ".img"
41 filter_glob = StringProperty(
42 options={'HIDDEN'},
43 default="*.img"
46 # Allow the user to specify a resolution factor for loading the
47 # terrain data at. This is useful because it allows the user to stage
48 # a scene with a low resolution terrain map, apply textures, modifiers,
49 # etc. and then increase the resolution to prepare for final rendering.
51 # Displaying this value as a percentage (0, 100] is an intuitive way
52 # for users to grasp what this value does. The DTM importer, however,
53 # wants to receive a value between (0, 1]. This is obviously a
54 # straightforward conversion:
56 # f(x) = x / 100
58 # But this conversion should happen here, in the terrain panel, rather
59 # than in the DTM importing utility itself. We can't pass get/set
60 # functions to the property itself because they result in a recursion
61 # error. Instead, we use another, hidden, property to store the scaled
62 # resolution.
63 dtm_resolution = FloatProperty(
64 subtype="PERCENTAGE",
65 description=(
66 "Percentage scale for terrain model resolution. 100\% loads the "
67 "model at full resolution (i.e. one vertex for each post in the "
68 "original terrain model) and is *MEMORY INTENSIVE*. Downsampling "
69 "uses Nearest Neighbors. You will be able to increase the "
70 "resolution of your mesh later, and still maintain all textures, "
71 "transformations, modifiers, etc., so best practice is to start "
72 "small. The downsampling algorithm may need to alter the "
73 "resolution you specify here to ensure it results in a whole "
74 "number of vertices. If it needs to alter the value you specify, "
75 "you are guaranteed that it will shrink it (i.e. decrease the "
76 "DTM resolution"
78 name="Terrain Model Resolution",
79 min=1.0, max=100.0, default=10.0
81 scaled_dtm_resolution = FloatProperty(
82 options={'HIDDEN'},
83 name="Scaled Terrain Model Resolution",
84 get=(lambda self: self.dtm_resolution / 100)
87 # HiRISE DTMs are huge, but it can be nice to load them in at scale. Here,
88 # we present the user with the option of setting up the Blender viewport
89 # to avoid a couple of common pitfalls encountered when working with such
90 # a large mesh.
92 # 1. The Blender viewport has a default clipping distance of 1km. HiRISE
93 # DTMs are often many kilometers in each direction. If this setting is
94 # not changed, an unsuspecting user may only see part (or even nothing
95 # at all) of the terrain. This option (true, by default) instructs
96 # Blender to change the clipping distance to something appropriate for
97 # the DTM, and scales the grid floor to have gridlines 1km apart,
98 # instead of 1m apart.
99 should_setup_viewport = BoolProperty(
100 description=(
101 "Set up the Blender screen to try and avoid clipping the DTM "
102 "and to make the grid floor larger. *WARNING* This will change "
103 "clipping distances and the Blender grid floor, and will fit the "
104 "DTM in the viewport"
106 name="Setup Blender Scene", default=True
108 # 2. Blender's default units are dimensionless. This option instructs
109 # Blender to change its unit's dimension to meters.
110 should_setup_units = BoolProperty(
111 description=(
112 "Set the Blender scene to use meters as its unit"
114 name="Set Blender Units to Meters", default=True
117 def execute(self, context):
118 """Runs when the "Import HiRISE Terrain Model" button is pressed"""
119 filepath = bpy.path.ensure_ext(self.filepath, self.filename_ext)
120 # Create a BTerrain from the DTM
121 dtm = DTM(filepath, self.scaled_dtm_resolution)
122 BTerrain.new(dtm)
124 # Set up the Blender UI
125 if self.should_setup_units:
126 self._setup_units(context)
127 if self.should_setup_viewport:
128 self._setup_viewport(context)
130 return {"FINISHED"}
132 def _setup_units(self, context):
133 """Sets up the Blender scene for viewing the DTM"""
134 scene = bpy.context.scene
136 # Set correct units
137 scene.unit_settings.system = 'METRIC'
138 scene.unit_settings.scale_length = 1.0
140 return {'FINISHED'}
142 def _setup_viewport(self, context):
143 """Sets up the Blender screen to make viewing the DTM easier"""
144 screen = bpy.context.screen
146 # Fetch the 3D_VIEW Area
147 for area in screen.areas:
148 if area.type == 'VIEW_3D':
149 space = area.spaces[0]
150 # Adjust 3D View Properties
151 # TODO: Can these be populated more intelligently?
152 space.clip_end = 100000
153 space.grid_scale = 1000
154 space.grid_lines = 50
156 # Fly to a nice view of the DTM
157 self._view_dtm(context)
159 return {'FINISHED'}
161 def _view_dtm(self, context):
162 """Sets up the Blender screen to make viewing the DTM easier"""
163 screen = bpy.context.screen
165 # Fetch the 3D_VIEW Area
166 for area in screen.areas:
167 if area.type == 'VIEW_3D':
168 # Move the camera around in the viewport. This requires
169 # a context override.
170 for region in area.regions:
171 if region.type == 'WINDOW':
172 override = {
173 'area': area,
174 'region': region,
175 'edit_object': bpy.context.edit_object
177 # Center View on DTM (SHORTCUT: '.')
178 bpy.ops.view3d.view_selected(override)
179 # Move to 'TOP' viewport (SHORTCUT: NUMPAD7)
180 bpy.ops.view3d.viewnumpad(override, type='TOP')
182 return {'FINISHED'}