Welcome to mirror list, hosted at ThFree Co, Russian Federation.

importer.py « ui « io_convert_image_to_mesh_img - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 35e82f62619f2e387d5c6709de67355ffa0abaa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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'}