makiolo

Generate HeightField

Jan 5th, 2012
232
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!BPY
  2. # coding: utf-8
  3. """
  4. Name: '********** Generate Heightfield ********** '
  5. Blender: 249
  6. Group: 'Export'
  7. Tooltip: 'Generate Heightfield'
  8. """
  9.  
  10. __author__ = ["Ricardo Marmolejo García"]
  11. __url__ = ("http://es.linkedin.com/in/ricardomarmolejogarcia", "https://twitter.com/makiolo", "http://http://blogricardo.wordpress.com/")
  12. __version__ = "1.00"
  13. __bpydoc__ = """\
  14. Ricardo Marmolejo García
  15. """
  16.  
  17. import Blender
  18. from Blender import *
  19. import bpy
  20. import math
  21. from BPyMesh import getMeshFromObject
  22. from BPyObject import getDerivedObjects
  23. import Image
  24. import PIL
  25.  
  26. # documentacion
  27. # www.blender.org/documentation/249PythonDoc/index.html
  28.  
  29. ############################ VARIABLES CONFIGURABLES #############
  30.  
  31. # ruta de la imagen con la información de altura
  32. heightfield = "/home/makiolo/Dropbox/heightfield/heightmap200.png"
  33.  
  34. # dimensiones del heightfield en unidades de juego (metro)
  35. width = 1000
  36. depth = 1000
  37. height = 100
  38.  
  39. ############################ AUXILIARES - NO TOCAR ############################
  40.  
  41. # hay que generar los vertex e indices del mesh
  42. vertex = []
  43. indexs = []
  44.  
  45. # datos de la imagen
  46. data = {}
  47.  
  48. # numero de vertices en ancho y fondo
  49. width_verts = 0
  50. depth_verts = 0
  51.  
  52. # numero de tiles
  53. width_tiles = 0
  54. depth_tiles = 0
  55.  
  56. # ancho y fondo de cada tile
  57. width_step = 0
  58. depth_step = 0
  59.  
  60. #################################################################################
  61.  
  62. # establecimiendo de globales mediante el tamaño de la imagen
  63. def setSizeInfo(_width, _height):
  64.    
  65.     global width_verts, depth_verts, width_tiles, depth_tiles, width_step, depth_step
  66.    
  67.     # numero de vertices en ancho y fondo
  68.     width_verts = _width
  69.     depth_verts = _height
  70.  
  71.     # numero de tiles
  72.     width_tiles = width_verts - 1
  73.     depth_tiles = depth_verts - 1
  74.  
  75.     # ancho y fondo de cada tile
  76.     width_step = width / width_tiles
  77.     depth_step = depth / depth_tiles
  78.  
  79. # funcion que determina la altura de cada vertice
  80. def getFunction(x, z):
  81.  
  82.     # La altura viene dada por la imagen
  83.     # aunque podría ser dada por una función matemática
  84.     y = data["%d-%d" % (x, z)]
  85.  
  86.     #y = math.sin(0.3 * x) * math.cos(0.1 * z)
  87.     return y
  88.  
  89. class VertexData:
  90.     def __init__(self, pos, uv):
  91.         self.pos = pos
  92.         self.uv = uv
  93.  
  94.     def __repr__(self):
  95.         return ("Posicion%s - UV%s" % (self.pos, self.uv))
  96.  
  97. class Vector2:
  98.     def __init__(self, u, v):
  99.         self.u = u
  100.         self.v = v
  101.  
  102.     def __repr__(self):
  103.         return ("(%.2f, %.2f)" % (self.u, self.v))
  104.  
  105. class Vector3:
  106.     def __init__(self, x, y, z):
  107.         self.x = x
  108.         self.y = y
  109.         self.z = z
  110.  
  111.     def __repr__(self):
  112.         return ("(%.2f, %.2f, %.2f)" % (self.x, self.y, self.z))
  113.  
  114. class Triangle:
  115.     def __init__(self, idx1, idx2, idx3):
  116.         self.idx1 = idx1
  117.         self.idx2 = idx2
  118.         self.idx3 = idx3
  119.  
  120.     def __repr__(self):
  121.         return ("Triangle (%d, %d, %d)" % (self.idx1, self.idx2, self.idx3))
  122.  
  123. def generate_heightfield():
  124.     editmode = Window.EditMode()    # are we in edit mode?  If so ...
  125.     if editmode: Window.EditMode(0) # leave edit mode before getting the mesh
  126.  
  127.     Blender.Window.DrawProgressBar(0.0, 'Generando heightfield.')
  128.  
  129.     # generar vertex
  130.     for c in range(width_verts):
  131.         for f in range(depth_verts):
  132.             vertex.append( VertexData(Vector3(  f * width_step,     # x
  133.                                 getFunction(f, c),                  # y
  134.                                 c * depth_step),                    # z
  135.                                 Vector2(float(f) / width_tiles,             # coord u
  136.                                 float(c) / depth_tiles)                     # coord v
  137.                         ))
  138.  
  139.     Blender.Window.DrawProgressBar(0.2, 'Generando heightfield.')
  140.  
  141.     # calcular maximo y minimo en altura
  142.     # se calcula la maxima amplitud
  143.     # normalizamos y escalamos a la altura solicitada
  144.     max_height = -9999999
  145.     min_height = +9999999
  146.  
  147.     for v in vertex:
  148.         if v.pos.y < min_height:
  149.             min_height = v.pos.y
  150.            
  151.         if v.pos.y > max_height:
  152.             max_height = v.pos.y
  153.  
  154.     distancia = max_height - min_height
  155.  
  156.     for v in vertex:
  157.         v.pos.y = (v.pos.y / distancia) * height
  158.        
  159.     Blender.Window.DrawProgressBar(0.4, 'Generando heightfield.')
  160.  
  161.     # calcular centro del mesh
  162.     center = Vector3(0, 0, 0)
  163.     count = 0
  164.    
  165.     for v in vertex:
  166.         center.x += v.pos.x
  167.         center.y += v.pos.y
  168.         center.z += v.pos.z
  169.         count = count + 1
  170.    
  171.     if count > 0:
  172.         center.x /= count
  173.         center.y /= count
  174.         center.z /= count
  175.        
  176.     Blender.Window.DrawProgressBar(0.6, 'Generando heightfield.')
  177.  
  178.     # centrar pivote
  179.     for v in vertex:
  180.         v.pos.x -= center.x
  181.         v.pos.y -= center.y
  182.         v.pos.z -= center.z
  183.        
  184.     Blender.Window.DrawProgressBar(0.7, 'Generando heightfield.')
  185.  
  186.     # generar indices
  187.     # A
  188.     #     i + j             ___         i + j + width_verts
  189.     #                       | /|
  190.     #     i + j + 1         |/_|        i + j + width_verts + 1
  191.     #    
  192.     #    
  193.     # B
  194.     #     i + j             ___         i + j + width_verts
  195.     #                       |\ |
  196.     #     i + j + 1         |_\|        i + j + width_verts + 1
  197.     #      
  198.  
  199.     (CLOCKWISE_A, CLOCKWISE_B,
  200.     ANTICLOCKWISE_A, ANTICLOCKWISE_B)=([ "%d" % i for i in range(4) ])
  201.  
  202.     mode = CLOCKWISE_A
  203.  
  204.     i = j = 0
  205.     for c in range(width_tiles): # z
  206.         for f in range(depth_tiles): # x
  207.            
  208.             if mode == CLOCKWISE_A:
  209.        
  210.                 indexs.append(Triangle(i + j,                          i + j + width_verts,          i + j + 1))
  211.                 indexs.append(Triangle(i + j + width_verts,            i + j + width_verts + 1,      i + j + 1))
  212.                
  213.             elif mode == CLOCKWISE_B:
  214.        
  215.                 indexs.append(Triangle(i + j,                          i + j + width_verts + 1,      i + j + 1))
  216.                 indexs.append(Triangle(i + j,                          i + j + width_verts,          i + j + width_verts + 1))
  217.                
  218.             if mode == ANTICLOCKWISE_A:
  219.        
  220.                 indexs.append(Triangle(i + j + 1,                           i + j + width_verts,          i + j))
  221.                 indexs.append(Triangle(i + j + 1,                           i + j + width_verts + 1,      i + j + width_verts))
  222.                
  223.             elif mode == ANTICLOCKWISE_B:
  224.        
  225.                 indexs.append(Triangle(i + j + 1,                           i + j + width_verts + 1,      i + j))
  226.                 indexs.append(Triangle(i + j + width_verts + 1,             i + j + width_verts,          i + j))
  227.  
  228.             i = i + 1
  229.        
  230.         j = j + 1
  231.        
  232.     Blender.Window.DrawProgressBar(0.9, 'Generando heightfield.')
  233.  
  234.     # mostrar resultados
  235.     coords = []  
  236.     faces = []
  237.    
  238.     for v in vertex:
  239.         coords.append( [v.pos.x, v.pos.y, v.pos.z] )
  240.    
  241.     for i in indexs:
  242.         faces.append( [i.idx1, i.idx2, i.idx3] )
  243.    
  244.     me = bpy.data.meshes.new('myMesh')          # create a new mesh
  245.  
  246.     me.addUVLayer("coord_uv")
  247.  
  248.     me.verts.extend(coords)
  249.     me.faces.extend(faces)
  250.    
  251.     '''
  252.     TODO:
  253.     Establecer colores a los vertices
  254.     Tengo calculadas las coordenadas UV pero no son asginadas
  255.    
  256.     me.vertexColors = 1
  257.     me.faces[1].col[0].r = 255
  258.     me.faces[1].col[1].g = 255
  259.     me.faces[1].col[2].b = 255
  260.     '''
  261.    
  262.     scn = bpy.data.scenes.active
  263.     ob = scn.objects.new(me, 'myHeightfield')
  264.  
  265.     me.update()
  266.     if editmode: Window.EditMode(1)
  267.     Blender.Redraw()
  268.    
  269.     Blender.Window.DrawProgressBar(1.0, 'Heightfield generado.')
  270.    
  271.     salida = "Heightfield generado\n"
  272.     Blender.Draw.PupMenu(salida)
  273.    
  274. def leer_imagen():
  275.     # lee la imagen con la informacion de altura
  276.     im = Image.open(heightfield)
  277.     #print im.format, im.size, im.mode
  278.     # Abre la imagen con el visor del SO
  279.     #im.show()
  280.    
  281.     _width = im.size[0]
  282.     _height = im.size[1]
  283.    
  284.     setSizeInfo(_width, _height)
  285.  
  286.     # grises 8 bits
  287.     if(im.mode == "L"):
  288.  
  289.         for y in range(_height):
  290.             for x in range(_width):
  291.                 pixel = im.getpixel( (x,y) )
  292.                 data["%d-%d" % (x,y)] = float(pixel)
  293.        
  294.         return True
  295.    
  296.     # RGB 32 bits
  297.     elif(im.mode == "RGB"):
  298.        
  299.         for y in range(_height):
  300.             for x in range(_width):
  301.                 pixel = im.getpixel( (x,y) )
  302.                 data["%d-%d" % (x,y)] = float(pixel[0] + pixel[1] + pixel[2]) / 3.0
  303.  
  304.         return True
  305.  
  306.     else:
  307.         print("Formato invalido")
  308.         return False
  309.  
  310. if __name__=='__main__':
  311.     if leer_imagen():
  312.         generate_heightfield()
  313.     else:
  314.         print("No se ha generado el mesh")
RAW Paste Data