Compare commits
2 Commits
5e92c691a8
...
9e9de119f8
| Author | SHA1 | Date |
|---|---|---|
|
|
9e9de119f8 | |
|
|
5d3676ee43 |
|
|
@ -9,7 +9,9 @@ TODO: make a proper build system.
|
||||||
For Blender 4.4:
|
For Blender 4.4:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip download rasterio --dest ./wheels --only-binary==:all: --python-version=3.11
|
pip download rasterio --dest ./wheels --only-binary=:all: --python-version=3.11 --platform=manylinux_2_17_x86_64
|
||||||
|
pip download rasterio --dest ./wheels --only-binary=:all: --python-version=3.11 --platform=macosx_11_0_arm64
|
||||||
|
pip download rasterio --dest ./wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
|
||||||
# Or replcace ".." below with any directory.
|
# Or replcace ".." below with any directory.
|
||||||
# blender_mapping_tools-0.1.0.zip will be placed in that directory.
|
# blender_mapping_tools-0.1.0.zip will be placed in that directory.
|
||||||
blender --command extension build --output-dir ..
|
blender --command extension build --output-dir ..
|
||||||
|
|
|
||||||
51
__init__.py
51
__init__.py
|
|
@ -1,7 +1,52 @@
|
||||||
from . import create_mesh_from_geotiff
|
import bpy
|
||||||
|
from bpy.props import FloatProperty, FloatVectorProperty, IntProperty, StringProperty
|
||||||
|
from .create_mesh_from_geotiff import OBJECT_OT_create_mesh_from_geotiff
|
||||||
|
from .import_geotiff_panel import OBJECT_PT_import_geotiff
|
||||||
|
from .select_geotiff_file import OBJECT_OT_select_geotiff_file
|
||||||
|
|
||||||
|
def init_scene_vars():
|
||||||
|
bpy.types.Scene.mapping_extension_geotiff_filename = StringProperty(
|
||||||
|
name="Geotiff filename",
|
||||||
|
description="Input GeoTIFF",
|
||||||
|
maxlen=1024, # Max length of the string.
|
||||||
|
options={'SKIP_SAVE'})
|
||||||
|
bpy.types.Scene.mapping_extension_map_origin = FloatVectorProperty(
|
||||||
|
name="Map origin",
|
||||||
|
description="The map coordinate that should map to the blender origin",
|
||||||
|
default=(0.0,0.0),
|
||||||
|
size=2,
|
||||||
|
options={'SKIP_SAVE'})
|
||||||
|
bpy.types.Scene.mapping_extension_map_scale = IntProperty(
|
||||||
|
name="Map Scale",
|
||||||
|
description="Denominator or map scale. E.g. for a 1:10,000 scale, set this to 10000",
|
||||||
|
default=10000,
|
||||||
|
min=1,
|
||||||
|
soft_max=1000000,
|
||||||
|
options={'SKIP_SAVE'})
|
||||||
|
bpy.types.Scene.mapping_extension_vertical_exaggeration = FloatProperty(
|
||||||
|
name="Map vertical exaggeration",
|
||||||
|
description="Scale applied to heights, on to of the map scale",
|
||||||
|
default=6.0,
|
||||||
|
min = 0.0,
|
||||||
|
soft_min = 1.0,
|
||||||
|
max = 1000000.0,
|
||||||
|
soft_max = 20.0,
|
||||||
|
options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def del_scene_vars():
|
||||||
|
del bpy.types.Scene.mapping_extension_geotiff_filename
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
create_mesh_from_geotiff.register()
|
init_scene_vars()
|
||||||
|
bpy.utils.register_class(OBJECT_PT_import_geotiff)
|
||||||
|
bpy.utils.register_class(OBJECT_OT_create_mesh_from_geotiff)
|
||||||
|
bpy.utils.register_class(OBJECT_OT_select_geotiff_file)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
create_mesh_from_geotiff.unregister()
|
del_scene_vars()
|
||||||
|
bpy.utils.unregister_class(OBJECT_PT_import_geotiff)
|
||||||
|
bpy.utils.unregister_class(OBJECT_OT_create_mesh_from_geotiff)
|
||||||
|
bpy.utils.unregister_class(OBJECT_OT_select_geotiff_file)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ wheels = [
|
||||||
"./wheels/click-8.2.1-py3-none-any.whl",
|
"./wheels/click-8.2.1-py3-none-any.whl",
|
||||||
"./wheels/click_plugins-1.1.1-py2.py3-none-any.whl",
|
"./wheels/click_plugins-1.1.1-py2.py3-none-any.whl",
|
||||||
"./wheels/cligj-0.7.2-py3-none-any.whl",
|
"./wheels/cligj-0.7.2-py3-none-any.whl",
|
||||||
|
"./wheels/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl",
|
||||||
"./wheels/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
|
"./wheels/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
|
||||||
|
"./wheels/numpy-2.2.6-cp311-cp311-win_amd64.whl",
|
||||||
"./wheels/pyparsing-3.2.3-py3-none-any.whl",
|
"./wheels/pyparsing-3.2.3-py3-none-any.whl",
|
||||||
"./wheels/rasterio-1.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
|
"./wheels/rasterio-1.4.1-cp311-cp311-macosx_11_0_arm64.whl",
|
||||||
|
"./wheels/rasterio-1.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
|
||||||
|
"./wheels/rasterio-1.4.3-cp311-cp311-win_amd64.whl"
|
||||||
]
|
]
|
||||||
|
|
@ -4,7 +4,7 @@ from bpy_extras.io_utils import ImportHelper
|
||||||
import rasterio
|
import rasterio
|
||||||
|
|
||||||
class GridVertexGenerator():
|
class GridVertexGenerator():
|
||||||
def __init__(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array):
|
def __init__(self, *, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array, map_scale, vertical_exaggeration):
|
||||||
self.cell_size_x = cell_size_x
|
self.cell_size_x = cell_size_x
|
||||||
if cell_size_y is not None:
|
if cell_size_y is not None:
|
||||||
self.cell_size_y = cell_size_y
|
self.cell_size_y = cell_size_y
|
||||||
|
|
@ -13,6 +13,8 @@ class GridVertexGenerator():
|
||||||
self.grid_size_x = grid_size_x
|
self.grid_size_x = grid_size_x
|
||||||
self.grid_size_y = grid_size_y
|
self.grid_size_y = grid_size_y
|
||||||
self.dem_array = dem_array
|
self.dem_array = dem_array
|
||||||
|
self.map_scale = map_scale
|
||||||
|
self.vertical_exaggeration = vertical_exaggeration
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.grid_size_x * self.grid_size_y
|
return self.grid_size_x * self.grid_size_y
|
||||||
|
|
@ -23,17 +25,12 @@ class GridVertexGenerator():
|
||||||
def _generator(self):
|
def _generator(self):
|
||||||
for j in range(self.grid_size_y):
|
for j in range(self.grid_size_y):
|
||||||
for i in range(self.grid_size_x):
|
for i in range(self.grid_size_x):
|
||||||
yield (i * self.cell_size_x,
|
yield (i * self.cell_size_x * self.map_scale,
|
||||||
j * self.cell_size_y,
|
j * self.cell_size_y * self.map_scale,
|
||||||
self.dem_array[j][i])
|
self.dem_array[j][i] * self.map_scale * self.vertical_exaggeration)
|
||||||
|
|
||||||
class GridTriangleGenerator():
|
class GridQuadGenerator():
|
||||||
def __init__(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y=None):
|
def __init__(self, grid_size_x, grid_size_y):
|
||||||
self.cell_size_x = cell_size_x
|
|
||||||
if cell_size_y is not None:
|
|
||||||
self.cell_size_y = cell_size_y
|
|
||||||
else:
|
|
||||||
self.cell_size_y = cell_size_x
|
|
||||||
self.grid_size_x = grid_size_x
|
self.grid_size_x = grid_size_x
|
||||||
self.grid_size_y = grid_size_y
|
self.grid_size_y = grid_size_y
|
||||||
|
|
||||||
|
|
@ -53,15 +50,21 @@ class GridTriangleGenerator():
|
||||||
yield (v00, v10, v11, v01)
|
yield (v00, v10, v11, v01)
|
||||||
|
|
||||||
|
|
||||||
class CreateMeshFromGeotiffOperator(bpy.types.Operator, ImportHelper):
|
class OBJECT_OT_create_mesh_from_geotiff(bpy.types.Operator):
|
||||||
"""Create a new object from a GeoTIFF"""
|
"""Create a new object from a GeoTIFF"""
|
||||||
bl_idname = "object.create_mesh_from_geotiff"
|
bl_idname = "object.create_mesh_from_geotiff"
|
||||||
bl_label = "Mesh from GeoTIFF"
|
bl_label = "Mesh from GeoTIFF"
|
||||||
|
|
||||||
filter_glob = StringProperty(default="*.tif;*.tiff", options={"HIDDEN"})
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.scene.mapping_extension_geotiff_filename
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
with rasterio.open(self.properties.filepath) as geotiff:
|
map_origin = context.scene.mapping_extension_map_origin
|
||||||
|
map_scale = 1.0 / context.scene.mapping_extension_map_scale
|
||||||
|
vertical_exaggeration = context.scene.mapping_extension_vertical_exaggeration
|
||||||
|
filename = context.scene.mapping_extension_geotiff_filename
|
||||||
|
with rasterio.open(filename) as geotiff:
|
||||||
grid_size_x = geotiff.width
|
grid_size_x = geotiff.width
|
||||||
grid_size_y = geotiff.height
|
grid_size_y = geotiff.height
|
||||||
print(f"GeoTIFF is {grid_size_x}x{grid_size_y}")
|
print(f"GeoTIFF is {grid_size_x}x{grid_size_y}")
|
||||||
|
|
@ -80,29 +83,30 @@ class CreateMeshFromGeotiffOperator(bpy.types.Operator, ImportHelper):
|
||||||
collection.objects.link(dem_obj)
|
collection.objects.link(dem_obj)
|
||||||
context.view_layer.objects.active = dem_obj
|
context.view_layer.objects.active = dem_obj
|
||||||
print("Creating mesh data...")
|
print("Creating mesh data...")
|
||||||
(vertices, edges, faces) = self.construct_mesh(grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array)
|
(vertices, edges, faces) = self.construct_mesh(grid_size_x=grid_size_x,
|
||||||
|
grid_size_y=grid_size_y,
|
||||||
|
cell_size_x=cell_size_x,
|
||||||
|
cell_size_y=cell_size_y,
|
||||||
|
dem_array=dem_array,
|
||||||
|
map_scale=map_scale,
|
||||||
|
vertical_exaggeration=vertical_exaggeration)
|
||||||
print("Constructing mesh...")
|
print("Constructing mesh...")
|
||||||
mesh.from_pydata(vertices, edges, faces, shade_flat=False)
|
mesh.from_pydata(vertices, edges, faces, shade_flat=False)
|
||||||
print("Done.")
|
print("Done.")
|
||||||
mesh.validate(verbose=True)
|
mesh.validate(verbose=True)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def construct_mesh(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array):
|
def construct_mesh(self, *, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array, map_scale, vertical_exaggeration):
|
||||||
return (GridVertexGenerator(grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array),
|
return (GridVertexGenerator(grid_size_x=grid_size_x,
|
||||||
|
grid_size_y=grid_size_y,
|
||||||
|
cell_size_x=cell_size_x,
|
||||||
|
cell_size_y=cell_size_y,
|
||||||
|
dem_array=dem_array,
|
||||||
|
map_scale=map_scale,
|
||||||
|
vertical_exaggeration=vertical_exaggeration),
|
||||||
[],
|
[],
|
||||||
GridTriangleGenerator(grid_size_x, grid_size_y, cell_size_x, cell_size_y))
|
GridQuadGenerator(grid_size_x, grid_size_y))
|
||||||
|
|
||||||
def menu_func(self, context):
|
def menu_func(self, context):
|
||||||
self.layout.operator(CreateMeshFromGeotiffOperator.bl_idname,
|
self.layout.operator(CreateMeshFromGeotiffOperator.bl_idname,
|
||||||
text=CreateMeshFromGeotiffOperator.bl_label)
|
text=CreateMeshFromGeotiffOperator.bl_label)
|
||||||
|
|
||||||
def register():
|
|
||||||
bpy.utils.register_class(CreateMeshFromGeotiffOperator)
|
|
||||||
bpy.types.VIEW3D_MT_add.append(menu_func)
|
|
||||||
|
|
||||||
def unregister():
|
|
||||||
bpy.utils.unregister_class(CreateMeshFromGeotiffOperator)
|
|
||||||
bpy.types.VIEW3D_MT_add.remove(menu_func)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
register()
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.props import StringProperty
|
||||||
|
|
||||||
|
from .create_mesh_from_geotiff import OBJECT_OT_create_mesh_from_geotiff
|
||||||
|
from .select_geotiff_file import OBJECT_OT_select_geotiff_file
|
||||||
|
|
||||||
|
class OBJECT_PT_import_geotiff(bpy.types.Panel):
|
||||||
|
"""Import GeoTIFF"""
|
||||||
|
bl_idname="IMPORT_GEOTIFF_PT_layout"
|
||||||
|
bl_label = "Import GeoTIFF"
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_category = "Mapping"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
column = self.layout.column()
|
||||||
|
geotiff_filename_row = column.row()
|
||||||
|
geotiff_filename_row.prop(context.scene, "mapping_extension_geotiff_filename")
|
||||||
|
geotiff_filename_row.operator(OBJECT_OT_select_geotiff_file.bl_idname,
|
||||||
|
icon='FILEBROWSER',
|
||||||
|
text='')
|
||||||
|
#column.prop(context.scene, "mapping_extension_map_origin")
|
||||||
|
column.prop(context.scene, "mapping_extension_map_scale")
|
||||||
|
column.prop(context.scene, "mapping_extension_vertical_exaggeration")
|
||||||
|
column.operator(OBJECT_OT_create_mesh_from_geotiff.bl_idname)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.props import StringProperty
|
||||||
|
from bpy_extras.io_utils import ImportHelper
|
||||||
|
|
||||||
|
class OBJECT_OT_select_geotiff_file(bpy.types.Operator, ImportHelper):
|
||||||
|
"""Display dialog to a GeoTIFF file for Mapping Tools to use."""
|
||||||
|
bl_idname = "object.select_geotiff_file"
|
||||||
|
bl_label = "Select GeoTIFF file"
|
||||||
|
|
||||||
|
filter_glob: StringProperty(default="*.tif;*.tiff", options={"HIDDEN"})
|
||||||
|
filepath: StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
context.scene.mapping_extension_geotiff_filename=self.properties.filepath
|
||||||
|
return {'FINISHED'}
|
||||||
Loading…
Reference in New Issue