Compare commits
No commits in common. "9e9de119f82b257c65c100349fa2db6aea27617c" and "5e92c691a8c35e86e9a148b78b48c38cd614d506" have entirely different histories.
9e9de119f8
...
5e92c691a8
|
|
@ -9,9 +9,7 @@ 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 --platform=manylinux_2_17_x86_64
|
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=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,52 +1,7 @@
|
||||||
import bpy
|
from . import create_mesh_from_geotiff
|
||||||
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():
|
||||||
init_scene_vars()
|
create_mesh_from_geotiff.register()
|
||||||
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():
|
||||||
del_scene_vars()
|
create_mesh_from_geotiff.unregister()
|
||||||
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,11 +20,7 @@ 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.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-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, map_scale, vertical_exaggeration):
|
def __init__(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array):
|
||||||
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,8 +13,6 @@ 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
|
||||||
|
|
@ -25,12 +23,17 @@ 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 * self.map_scale,
|
yield (i * self.cell_size_x,
|
||||||
j * self.cell_size_y * self.map_scale,
|
j * self.cell_size_y,
|
||||||
self.dem_array[j][i] * self.map_scale * self.vertical_exaggeration)
|
self.dem_array[j][i])
|
||||||
|
|
||||||
class GridQuadGenerator():
|
class GridTriangleGenerator():
|
||||||
def __init__(self, grid_size_x, grid_size_y):
|
def __init__(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y=None):
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -50,21 +53,15 @@ class GridQuadGenerator():
|
||||||
yield (v00, v10, v11, v01)
|
yield (v00, v10, v11, v01)
|
||||||
|
|
||||||
|
|
||||||
class OBJECT_OT_create_mesh_from_geotiff(bpy.types.Operator):
|
class CreateMeshFromGeotiffOperator(bpy.types.Operator, ImportHelper):
|
||||||
"""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"
|
||||||
|
|
||||||
@classmethod
|
filter_glob = StringProperty(default="*.tif;*.tiff", options={"HIDDEN"})
|
||||||
def poll(cls, context):
|
|
||||||
return context.scene.mapping_extension_geotiff_filename
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
map_origin = context.scene.mapping_extension_map_origin
|
with rasterio.open(self.properties.filepath) as geotiff:
|
||||||
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}")
|
||||||
|
|
@ -83,30 +80,29 @@ class OBJECT_OT_create_mesh_from_geotiff(bpy.types.Operator):
|
||||||
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_x,
|
(vertices, edges, faces) = self.construct_mesh(grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array)
|
||||||
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, map_scale, vertical_exaggeration):
|
def construct_mesh(self, grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array):
|
||||||
return (GridVertexGenerator(grid_size_x=grid_size_x,
|
return (GridVertexGenerator(grid_size_x, grid_size_y, cell_size_x, cell_size_y, dem_array),
|
||||||
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),
|
|
||||||
[],
|
[],
|
||||||
GridQuadGenerator(grid_size_x, grid_size_y))
|
GridTriangleGenerator(grid_size_x, grid_size_y, cell_size_x, cell_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()
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
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