BlenderGIS
🎃问题描述
- 🎃问题描述
- 🎳解决方案
🎈在BlenderGIS中导入tif、png、jpg等格式的数据时会有报错的情况发生,如果遇到了以下情况可以按本文的方案修改~
按提示的路径打开对应的脚本(推荐用VS Code) 🎁 用一下内容覆盖保存后重启Blender即可!
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# 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 .
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import os
import numpy as np
import bpy, bmesh
import math
import logging
log = logging.getLogger(__name__)
from ...core.georaster import GeoRaster
def _exportAsMesh(georaster, dx=0, dy=0, step=1, buildFaces=True, flat=False, subset=False, reproj=None):
'''Numpy test'''
if subset and georaster.subBoxGeo is None:
subset = False
if not subset:
georef = georaster.georef
else:
georef = georaster.getSubBoxGeoRef()
x0, y0 = georef.origin #pxcenter
pxSizeX, pxSizeY = georef.pxSize.x, georef.pxSize.y
w, h = georef.rSize.x, georef.rSize.y
#adjust against step
w, h = math.ceil(w/step), math.ceil(h/step)
pxSizeX, pxSizeY = pxSizeX * step, pxSizeY * step
x = np.array([(x0 + (pxSizeX * i)) - dx for i in range(0, w)])
y = np.array([(y0 + (pxSizeY * i)) - dy for i in range(0, h)])
xx, yy = np.meshgrid(x, y)
#TODO reproj
if flat:
zz = np.zeros((h, w))
else:
zz = georaster.readAsNpArray(subset=subset).data[::step,::step] #TODO raise error if multiband
verts = np.column_stack((xx.ravel(), yy.ravel(), zz.ravel()))
if buildFaces:
faces = [(x+y*w, x+y*w+1, x+y*w+1+w, x+y*w+w) for x in range(0, w-1) for y in range(0, h-1)]
else:
faces = []
mesh = bpy.data.meshes.new("DEM")
mesh.from_pydata(verts, [], faces)
mesh.update()
return mesh
def exportAsMesh(georaster, dx=0, dy=0, step=1, buildFaces=True, subset=False, reproj=None, flat=False):
if subset and georaster.subBoxGeo is None:
subset = False
if not subset:
georef = georaster.georef
else:
georef = georaster.getSubBoxGeoRef()
if not flat:
img = georaster.readAsNpArray(subset=subset)
#TODO raise error if multiband
data = img.data
x0, y0 = georef.origin #pxcenter
pxSizeX, pxSizeY = georef.pxSize.x, georef.pxSize.y
w, h = georef.rSize.x, georef.rSize.y
#Build the mesh (Note : avoid using bmesh because it's very slow with large mesh, use from_pydata instead)
verts = []
faces = []
nodata = []
idxMap = {}
for py in range(0, h, step):
for px in range(0, w, step):
x = x0 + (pxSizeX * px)
y = y0 + (pxSizeY * py)
if reproj is not None:
x, y = reproj.pt(x, y)
#shift
x -= dx
y -= dy
if flat:
z = 0
else:
z = data[py, px]
#vertex index
v1 = px + py * w #bottom right
#Filter nodata
if z == georaster.noData:
nodata.append(v1)
else:
verts.append((x, y, z))
idxMap[v1] = len(verts) - 1
#build face from bottomright to topright (using only points already created)
if buildFaces and px > 0 and py > 0: #filter first row and column
v2 = v1 - step #bottom left
v3 = v2 - w * step #topleft
v4 = v3 + step #topright
f = [v4, v3, v2, v1] #anticlockwise --> face up
if not any(v in f for v in nodata): #TODO too slow ?
f = [idxMap[v] for v in f]
faces.append(f)
mesh = bpy.data.meshes.new("DEM")
mesh.from_pydata(verts, [], faces)
mesh.update()
return mesh
def rasterExtentToMesh(name, rast, dx, dy, pxLoc='CORNER', reproj=None, subdivise=False):
'''Build a new mesh that represent a georaster extent'''
#create mesh
bm = bmesh.new()
if pxLoc == 'CORNER':
pts = [(pt[0], pt[1]) for pt in rast.corners]#shift coords
elif pxLoc == 'CENTER':
pts = [(pt[0], pt[1]) for pt in rast.cornersCenter]
#Reprojection
if reproj is not None:
pts = reproj.pts(pts)
#build shifted flat 3d vertices
pts = [bm.verts.new((pt[0]-dx, pt[1]-dy, 0)) for pt in pts]#upper left to botton left (clockwise)
pts.reverse()#bottom left to upper left (anticlockwise --> face up)
bm.faces.new(pts)
#Create mesh from bmesh
mesh = bpy.data.meshes.new(name)
bm.to_mesh(mesh)
bm.free()
return mesh
def geoRastUVmap(obj, uvLayer, rast, dx, dy, reproj=None):
'''uv map a georaster texture on a given mesh'''
mesh = obj.data
#Assign uv coords
loc = obj.location
for pg in mesh.polygons:
for i in pg.loop_indices:
vertIdx = mesh.loops[i].vertex_index
pt = list(mesh.vertices[vertIdx].co)
#adjust coords against object location and shift values to retrieve original point coords
pt = (pt[0] + loc.x + dx, pt[1] + loc.y + dy)
if reproj is not None:
pt = reproj.pt(*pt)
#Compute UV coords --> pourcent from image origin (bottom left)
dx_px, dy_px = rast.pxFromGeo(pt[0], pt[1], reverseY=True, round2Floor=False)
u = dx_px / rast.size[0]
v = dy_px / rast.size[1]
#Assign coords
#uvLoop = uvLoopLayer.data[i]
#uvLoop.uv = [u,v]
if(hasattr(uvLayer,data)):
uvLayer.data[i].uv = [u,v]
def setDisplacer(obj, rast, uvTxtLayer, mid=0, interpolation=False):
#Config displacer
displacer = obj.modifiers.new('DEM', type='DISPLACE')
demTex = bpy.data.textures.new('demText', type = 'IMAGE')
demTex.image = rast.bpyImg
demTex.use_interpolation = interpolation
demTex.extension = 'CLIP'
demTex.use_clamp = False #Needed to get negative displacement with float32 texture
displacer.texture = demTex
displacer.texture_coords = 'UV'
displacer.uv_layer = uvTxtLayer.name
displacer.mid_level = mid #Texture values below this value will result in negative displacement
#Setting the displacement strength :
#displacement = (texture value - Midlevel) * Strength
#>> Strength = displacement / texture value (because mid=0)
#If DEM non scaled then
# *displacement = alt max - alt min = delta Z
# *texture value = delta Z / (2^depth-1)
# (because in Blender, pixel values are normalized between 0.0 and 1.0)
#>> Strength = delta Z / (delta Z / (2^depth-1))
#>> Strength = 2^depth-1
if rast is None ==False and rast.depth 32767, -(65536-a), a)
'''
if not subset:
return a
else:
# Get overlay extent (in pixels)
subBoxPx = self.subBoxPx
# Get subset data (min and max pixel number are both include)
a = a[subBoxPx.ymin:subBoxPx.ymax+1, subBoxPx.xmin:subBoxPx.xmax+1] #topleft to bottomright
return a
def flattenPixelsArray(self, px):
'''
Flatten a 3d array of pixels to match the shape of bpy.pixels
[ [[rgba], [rgba]...], [lines2], [lines3]...] >> [r,g,b,a,r,g,b,a,r,g,b,a, ... ]
If the submited array contains only one band, then the band will be duplicate
and an alpha band will be added to get all rgba values.
'''
shape = px.shape
if len(shape) == 2:
px = np.expand_dims(px, axis=2)
px = np.repeat(px, 3, axis=2)
alpha = np.ones(shape)
alpha = np.expand_dims(alpha, axis=2)
px = np.append(px, alpha, axis=2)
#px = px.swapaxes(0,1)
px = np.flipud(px)
px = px.flatten()
return px