208 lines
8.1 KiB
Python
208 lines
8.1 KiB
Python
"""
|
|
Heightmap.
|
|
http://www.cs.otago.ac.nz/graphics/Mirage/node59.html
|
|
http://en.wikipedia.org/wiki/Heightmap
|
|
http://en.wikipedia.org/wiki/Netpbm_format
|
|
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
|
|
import __init__
|
|
|
|
from fabmetheus_utilities.geometry.creation import lineation
|
|
from fabmetheus_utilities.geometry.creation import solid
|
|
from fabmetheus_utilities.geometry.geometry_tools import path
|
|
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
|
from fabmetheus_utilities.geometry.solids import triangle_mesh
|
|
from fabmetheus_utilities.vector3 import Vector3
|
|
from fabmetheus_utilities.vector3index import Vector3Index
|
|
from fabmetheus_utilities import archive
|
|
from fabmetheus_utilities import euclidean
|
|
import math
|
|
import random
|
|
|
|
|
|
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
|
|
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
|
|
__date__ = '$Date: 2008/02/05 $'
|
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
|
|
|
|
|
def addHeightsByBitmap(heights, textLines):
|
|
'Add heights by bitmap.'
|
|
for line in textLines[3:]:
|
|
for integerWord in line.split():
|
|
heights.append(float(integerWord))
|
|
|
|
def addHeightsByGraymap(heights, textLines):
|
|
'Add heights by graymap.'
|
|
divisor = float(textLines[3])
|
|
for line in textLines[4:]:
|
|
for integerWord in line.split():
|
|
heights.append(float(integerWord) / divisor)
|
|
|
|
def getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes):
|
|
'Get and add an indexed heightGrid.'
|
|
indexedHeightGrid = []
|
|
for rowIndex, row in enumerate(heightGrid):
|
|
indexedRow = []
|
|
indexedHeightGrid.append(indexedRow)
|
|
rowOffset = step.imag * float(rowIndex) + minimumXY.imag
|
|
for columnIndex, element in enumerate(row):
|
|
columnOffset = step.real * float(columnIndex) + minimumXY.real
|
|
vector3index = Vector3Index(len(vertexes), columnOffset, rowOffset, top * element)
|
|
indexedRow.append(vector3index)
|
|
vertexes.append(vector3index)
|
|
return indexedHeightGrid
|
|
|
|
def getAddIndexedSegmentedPerimeter(heightGrid, maximumXY, minimumXY, step, vertexes, z=0.0):
|
|
'Get and add an indexed segmented perimeter.'
|
|
indexedSegmentedPerimeter = []
|
|
firstRow = heightGrid[0]
|
|
columnOffset = minimumXY.real
|
|
numberOfRowsMinusTwo = len(heightGrid) - 2
|
|
for column in firstRow:
|
|
vector3index = Vector3Index(len(vertexes), columnOffset, minimumXY.imag, z)
|
|
vertexes.append(vector3index)
|
|
indexedSegmentedPerimeter.append(vector3index)
|
|
columnOffset += step.real
|
|
rowOffset = minimumXY.imag
|
|
for rowIndex in xrange(numberOfRowsMinusTwo):
|
|
rowOffset += step.imag
|
|
vector3index = Vector3Index(len(vertexes), maximumXY.real, rowOffset, z)
|
|
vertexes.append(vector3index)
|
|
indexedSegmentedPerimeter.append(vector3index)
|
|
columnOffset = maximumXY.real
|
|
for column in firstRow:
|
|
vector3index = Vector3Index(len(vertexes), columnOffset, maximumXY.imag, z)
|
|
vertexes.append(vector3index)
|
|
indexedSegmentedPerimeter.append(vector3index)
|
|
columnOffset -= step.real
|
|
rowOffset = maximumXY.imag
|
|
for rowIndex in xrange(numberOfRowsMinusTwo):
|
|
rowOffset -= step.imag
|
|
vector3index = Vector3Index(len(vertexes), minimumXY.real, rowOffset, z)
|
|
vertexes.append(vector3index)
|
|
indexedSegmentedPerimeter.append(vector3index)
|
|
return indexedSegmentedPerimeter
|
|
|
|
def getGeometryOutput(elementNode):
|
|
'Get vector3 vertexes from attribute dictionary.'
|
|
derivation = HeightmapDerivation(elementNode)
|
|
heightGrid = derivation.heightGrid
|
|
if derivation.fileName != '':
|
|
heightGrid = getHeightGrid(archive.getAbsoluteFolderPath(elementNode.getOwnerDocument().fileName, derivation.fileName))
|
|
return getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid)
|
|
|
|
def getGeometryOutputByArguments(arguments, elementNode):
|
|
'Get vector3 vertexes from attribute dictionary by arguments.'
|
|
evaluate.setAttributesByArguments(['file', 'start'], arguments, elementNode)
|
|
return getGeometryOutput(elementNode)
|
|
|
|
def getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid):
|
|
'Get vector3 vertexes from attribute dictionary.'
|
|
numberOfColumns = len(heightGrid)
|
|
if numberOfColumns < 2:
|
|
print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two rows for:')
|
|
print(heightGrid)
|
|
print(elementNode)
|
|
return None
|
|
numberOfRows = len(heightGrid[0])
|
|
if numberOfRows < 2:
|
|
print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two columns for:')
|
|
print(heightGrid)
|
|
print(elementNode)
|
|
return None
|
|
for row in heightGrid:
|
|
if len(row) != numberOfRows:
|
|
print('Warning, in getGeometryOutputByHeightGrid in heightmap the heightgrid is not rectangular for:')
|
|
print(heightGrid)
|
|
print(elementNode)
|
|
return None
|
|
inradiusComplex = derivation.inradius.dropAxis()
|
|
minimumXY = -inradiusComplex
|
|
step = complex(derivation.inradius.x / float(numberOfRows - 1), derivation.inradius.y / float(numberOfColumns - 1))
|
|
step += step
|
|
faces = []
|
|
heightGrid = getRaisedHeightGrid(heightGrid, derivation.start)
|
|
top = derivation.inradius.z + derivation.inradius.z
|
|
vertexes = []
|
|
indexedBottomLoop = getAddIndexedSegmentedPerimeter(heightGrid, inradiusComplex, minimumXY, step, vertexes)
|
|
indexedLoops = [indexedBottomLoop]
|
|
indexedGridTop = getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes)
|
|
indexedLoops.append(triangle_mesh.getIndexedLoopFromIndexedGrid(indexedGridTop))
|
|
vertexes = triangle_mesh.getUniqueVertexes(indexedLoops + indexedGridTop)
|
|
triangle_mesh.addPillarFromConvexLoopsGridTop(faces, indexedGridTop, indexedLoops)
|
|
return triangle_mesh.getGeometryOutputByFacesVertexes(faces, vertexes)
|
|
|
|
def getHeightGrid(fileName):
|
|
'Get heightGrid by fileName.'
|
|
if 'models/' not in fileName:
|
|
print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:')
|
|
print(fileName)
|
|
print('The heightmap tool can only read a file which has models/ in the file path.')
|
|
print('To import the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.')
|
|
return
|
|
pgmText = archive.getFileText(fileName)
|
|
textLines = archive.getTextLines(pgmText)
|
|
format = textLines[0].lower()
|
|
sizeWords = textLines[2].split()
|
|
numberOfColumns = int(sizeWords[0])
|
|
numberOfRows = int(sizeWords[1])
|
|
heights = []
|
|
if format == 'p1':
|
|
addHeightsByBitmap(heights, textLines)
|
|
elif format == 'p2':
|
|
addHeightsByGraymap(heights, textLines)
|
|
else:
|
|
print('Warning, the file format was not recognized for:')
|
|
print(fileName)
|
|
print('Heightmap can only read the Netpbm Portable bitmap format and the Netpbm Portable graymap format.')
|
|
print('The Netpbm formats are described at:')
|
|
print('http://en.wikipedia.org/wiki/Netpbm_format')
|
|
return []
|
|
heightGrid = []
|
|
heightIndex = 0
|
|
for rowIndex in xrange(numberOfRows):
|
|
row = []
|
|
heightGrid.append(row)
|
|
for columnIndex in xrange(numberOfColumns):
|
|
row.append(heights[heightIndex])
|
|
heightIndex += 1
|
|
return heightGrid
|
|
|
|
def getNewDerivation(elementNode):
|
|
'Get new derivation.'
|
|
return HeightmapDerivation(elementNode)
|
|
|
|
def getRaisedHeightGrid(heightGrid, start):
|
|
'Get heightGrid raised above start.'
|
|
raisedHeightGrid = []
|
|
remainingHeight = 1.0 - start
|
|
for row in heightGrid:
|
|
raisedRow = []
|
|
raisedHeightGrid.append(raisedRow)
|
|
for element in row:
|
|
raisedElement = remainingHeight * element + start
|
|
raisedRow.append(raisedElement)
|
|
return raisedHeightGrid
|
|
|
|
def processElementNode(elementNode):
|
|
'Process the xml element.'
|
|
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode))
|
|
|
|
|
|
class HeightmapDerivation:
|
|
'Class to hold heightmap variables.'
|
|
def __init__(self, elementNode):
|
|
'Set defaults.'
|
|
self.fileName = evaluate.getEvaluatedString('', elementNode, 'file')
|
|
self.heightGrid = evaluate.getEvaluatedValue([], elementNode, 'heightGrid')
|
|
self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius'], Vector3(10.0, 10.0, 5.0))
|
|
self.inradius = evaluate.getVector3ByMultiplierPrefix(elementNode, 2.0, 'size', self.inradius)
|
|
self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
|
|
|
|
def __repr__(self):
|
|
'Get the string representation of this HeightmapDerivation.'
|
|
return euclidean.getDictionaryString(self.__dict__)
|