#!BPY

# """
# Name: 'Stereogram'
# Blender: 245
# Group: 'Wizards'
# Tooltip: 'Generate stereogram from rendered heightmap and a pattern image.'
# """

__author__ = 'Peter Sulyok'
__version__ = '1.0test4 2008-02-05'
__url__ = ["Author's site, http://sp.web.elte.hu/",
           "Support forum, http://blenderartists.org/forum/"]
__email__ = ["Peter Sulyok, peti:sulyok*hu", "scripts"]
__bpydoc__ = """\
 This script generates stereogram from rendered heightmap and a pattern image.
 
 Beginning:
 download stereogram.blend from<br>
 http://www.sulyok.hu/~peti/blender-stereogram/stereogram.blend<br>
 Choose Scripts->Wizards->Stereogram menu item in the Scripts Window.<br>
 This creates a new scene called stereogram, and renders it.<br>
 
 Create your own stereograms:
 Remove all objects, and add/import objects. Arrange your objects in front<br>
 of the camera. Select them, WKEY->Union, render (F12). Have a look at the<br>
 rendered heightmap. If it looks fine, create the stereogram choosing<br>
 Scripts->Wizards->Stereogram.

 Known issues:<br>
   None

"""

import Blender
from Blender import Camera
from Blender import Material
from Blender import Mesh
from Blender import Object
from Blender import Scene
from Blender import Texture
from Blender import Window
from Blender.Draw import *
from Blender.BGL import *
from random import *
from math import floor

EVENT_NOEVENT = 1
EVENT_IMAGE = 2
EVENT_ANIM = 3
EVENT_EXIT = 4
EVENT_SEL_PAT = 11
EVENT_SET_PAT = 12
EVENT_SET_LEVELS = 21
EVENT_SET_DEPTH = 22
EVENT_SET_PATW = 23
EVENT_SET_METHOD = 24

hm_scene = Scene.Get('heightmap')
hm_rd = hm_scene.getRenderingContext()
pat_fn = Create('Plasm')
#pat_fn = Create('Random')
#pat_fn = Create('/home/peti/works/blender/Tile9-original.jpg')
levels = Create(15)
depth = Create(1)
pat_w = Create(100)
method = 1

def get_heightmap():
  hm_scene.makeCurrent()
  hm_rd.render()
  hm_rd.setImageType(Scene.Render.PNG)
  hm_rd.saveRenderedImage('stereogram-heightmap.png')
  img = Blender.Image.Load(hm_rd.renderPath+'stereogram-heightmap.png')
  Blender.Image.Get('stereogram-heightmap.').reload()
  return img

def put_pixel_lr(hm, stg, pat, pw, ph):
  (width, height) = hm.getSize()
  lq = 256 / levels.val
  df = depth.val
  for y in range(height):
    for x in range(pw): stg.setPixelI(x, y, pat.getPixelI(x, y%ph))
    hx = pw / 2
    for sx in range (pw, width):
      offset = pw - hm.getPixelI(hx, y)[0] / lq * df
      stg.setPixelI(sx, y, stg.getPixelI(sx-offset, y))
      hx = hx + 1

def put_pixel_bt(hm, stg, pat, pw, ph):
  (width, height) = hm.getSize()
  lq = 256 / levels.val
  df = depth.val
  mid = (width-1) / pw / 2
  start = mid * pw
  if 10<levels.val: filler = levels.val / 5
  else: filler=5
  for y in range(height):
    a = [x for x in range(width)]
    b = [0 for x in range(width)]
    hx = pw / 2
    for sx in range (pw, width):
      offset = pw - hm.getPixelI(hx, y)[0] / lq * df
      a[sx] = sx-offset
      hx = hx + 1
    for x in range(pw): 
      stg.setPixelI(x+start, y, pat.getPixelI(x, y%ph))
      b[x+start] = 1
    hx = pw / 2 + mid*pw
    for sx in range (start+pw, width):
      offset = pw - hm.getPixelI(hx, y)[0] / lq * df
      stg.setPixelI(sx, y, stg.getPixelI(sx-offset, y))
      hx = hx + 1
    for sx in range (start+pw-1, pw, -1):
      if 0==b[sx]: stg.setPixelI(sx, y, stg.getPixelI(sx+filler, y))
      b[a[sx]] = 1
      stg.setPixelI(a[sx], y, stg.getPixelI(sx, y))

def random_dots(pw, ph, depth):
  pat = Blender.Image.New('rand-pat', pw, ph, depth)
  for y in range(ph):
    for x in range(pw):
      pat.setPixelI(x, y, (randint(0,255),randint(0,255),randint(0,255),1))
  return pat

def plasm_dots(pw, ph, depth):
  pat = Blender.Image.New('rand-pat', pw, ph, depth)
  sf = 5
  pdr = [[randint(0,255) for j in range(ph/sf+2)] for i in range(pw/sf+1)]
  pdg = [[randint(0,255) for j in range(ph/sf+2)] for i in range(pw/sf+1)]
  pdb = [[randint(0,255) for j in range(ph/sf+2)] for i in range(pw/sf+1)]
  for x in range(pw/sf): (pdr[x][ph/sf],pdg[x][ph/sf],pdb[x][ph/sf])=(pdr[x][0],pdg[x][0],pdb[x][0])
  for y in range(ph/sf+1): (pdr[pw/sf][y],pdg[pw/sf][y],pdb[pw/sf][y])=(pdr[0][y],pdg[0][y],pdb[0][y])
  for y in range(ph):
    y0 = y / sf
    y1 = y0 + 1
    fy = y % sf
    fy0 = sf - fy
    for x in range(pw):
      x0 = x / sf
      x1 = x0 + 1
      fx = x % sf
      fx0 = sf - fx
      r0 = (fy0*pdr[x0][y0] + fy*pdr[x0][y1]) / sf
      r1 = (fy0*pdr[x1][y0] + fy*pdr[x1][y1]) / sf
      r = (fx0*r0 + fx*r1) / sf
      g0 = (fy0*pdg[x0][y0] + fy*pdg[x0][y1]) / sf
      g1 = (fy0*pdg[x1][y0] + fy*pdg[x1][y1]) / sf
      g = (fx0*g0 + fx*g1) / sf
      b0 = (fy0*pdb[x0][y0] + fy*pdb[x0][y1]) / sf
      b1 = (fy0*pdb[x1][y0] + fy*pdb[x1][y1]) / sf
      b = (fx0*b0 + fx*b1) / sf
      pat.setPixelI(x, y, (r,g,b,1))
  return pat

def generate_image(pat=None):
  global stg_scene,st_rd
  hm = get_heightmap()
  (width, height) = hm.getSize()
  Window.WaitCursor(1)
  if pat_fn.val == 'Random':
    if pat==None: pat = random_dots(pat_w.val, height, hm.getDepth())
    pw = pat_w.val
    ph = height
  elif pat_fn.val == 'Plasm':
    if pat==None: pat = plasm_dots(pat_w.val, height, hm.getDepth())
    pw = pat_w.val
    ph = height
  else:
    if pat==None: pat = Blender.Image.Load(pat_fn.val)
    (pw, ph) = pat.getSize()
    if pat_w.val < pw: pw = pat_w.val
    if depth.val > pw/3: depth.val=pw/3
  try: stg = Blender.Image.Get('stereogram_image')
  except: stg = Blender.Image.New('stereogram_image', width, height, hm.getDepth())
  if 1==method: put_pixel_lr(hm, stg, pat, pw, ph)
  else: put_pixel_bt(hm, stg, pat, pw, ph)
  try: stg_scene = Scene.Get('stereogram')
  except: stg_scene = Scene.New('stereogram')
  st_rd = stg_scene.getRenderingContext()
  st_rd.imageType = hm_rd.imageType
  st_rd.imageSizeX(hm_rd.imageSizeX())
  st_rd.aspectRatioX(hm_rd.aspectRatioX())
  st_rd.imageSizeY(hm_rd.imageSizeY())
  st_rd.aspectRatioY(hm_rd.aspectRatioY())
  st_rd.enableOversampling(0)
  try: cam = Camera.Get('stereogram_camera')
  except:
    cam = Camera.New('ortho', 'stereogram_camera')
    cam.scale = 2.0
    stg_scene.objects.camera = stg_scene.objects.new(cam)
    stg_scene.objects.camera.setLocation (0.0, 0.0, 1.0)
  try: me = Mesh.Get('stereogram_mesh')
  except:
    me = Mesh.New('stereogram_mesh')
    me.verts.extend([ [1,1,0], [-1,1,0], [-1,-1,0], [1,-1,0] ])
    me.faces.extend([ [0,1,2,3] ])
  try: mat = Material.Get('stereogram_material')
  except:
    mat = Material.New('stereogram_material')
    mat.rgbCol = [1.0, 1.0, 1.0]
    mat.mode = Material.Modes.SHADELESS
  try: tex = Texture.Get('stereogram_texture')
  except:
    tex = Texture.New('stereogram_texture')
    tex.setType('Image')
    tex.image = stg
    mat.setTexture(0, tex)
  try: po = Object.Get('stereogram_plane')
  except:
    po = stg_scene.objects.new(me, 'stereogram_plane')
    po.setSize(1.0, 1.0 * st_rd.imageSizeY() * st_rd.aspectRatioY() / (st_rd.imageSizeX() * st_rd.aspectRatioX()), 1.0)
    po.setMaterials([mat])
    po.colbits = 1
  st_rd.render()
  stg_scene.makeCurrent()
  Window.WaitCursor(0)
  return pat

def generate_animation():
  hm_scene.makeCurrent()
  pat = None
  for frame in range(hm_rd.sFrame, hm_rd.eFrame+1):
    if frame<10: fill='00'
    elif frame<100: fill='0'
    else: fill=''
    img_name = 'stereogram'+fill+str(frame)+'.png'
    hm_rd.cFrame = frame
    pat = generate_image(pat)
    st_rd.saveRenderedImage(img_name)
  try: sta_scene = Scene.Get('stereogranim')
  except: sta_scene = Scene.New('stereogranim')
  sta_rd = sta_scene.getRenderingContext()
  sta_rd.imageType = Scene.Render.AVIJPEG
  sta_rd.fps = hm_rd.fps
  sta_rd.imageSizeX(hm_rd.imageSizeX())
  sta_rd.aspectRatioX(hm_rd.aspectRatioX())
  sta_rd.imageSizeY(hm_rd.imageSizeY())
  sta_rd.aspectRatioY(hm_rd.aspectRatioY())
  sta_rd.enableOversampling(0)
  try: cam = Camera.Get('stereogranim_camera')
  except:
    cam = Camera.New('ortho', 'stereogranim_camera')
    cam.scale = 2.0
    sta_scene.objects.camera = sta_scene.objects.new(cam)
    sta_scene.objects.camera.setLocation (0.0, 0.0, 1.0)
  try: me = Mesh.Get('stereogranim_mesh')
  except:
    me = Mesh.New('stereogranim_mesh')
    me.verts.extend([ [1,1,0], [-1,1,0], [-1,-1,0], [1,-1,0] ])
    me.faces.extend([ [0,1,2,3] ])
  try: mat = Material.Get('stereogranim_material')
  except:
    mat = Material.New('stereogranim_material')
    mat.rgbCol = [1.0, 1.0, 1.0]
    mat.mode = Material.Modes.SHADELESS
  try: tex = Texture.Get('stereogranim_texture')
  except:
    tex = Texture.New('stereogranim_texture')
    mat.setTexture(0, tex)
  try: po = Object.Get('stereogranim_plane')
  except:
    po = sta_scene.objects.new(me, 'stereogranim_plane')
    po.setSize(1.0, 1.0 * sta_rd.imageSizeY() * sta_rd.aspectRatioY() / (sta_rd.imageSizeX() * sta_rd.aspectRatioX()), 1.0)
    po.setMaterials([mat])
    po.colbits = 1
  tex.setType('Image')
  tex.image = Blender.Image.Load(st_rd.renderPath+'stereogram001.png')
  tex.image.reload()
  tex.animFrames = hm_rd.eFrame - hm_rd.sFrame + 1
  tex.image.source = Blender.Image.Sources['SEQUENCE']
  sta_rd.sFrame = hm_rd.sFrame
  sta_rd.eFrame = hm_rd.eFrame
  sta_rd.imageType = Scene.Render.AVIJPEG
  sta_rd.renderAnim()
  sta_scene.makeCurrent()

def sel_pat_file(filename):
  pat_fn.val = filename

def sel_stg_file(filename):
  stg_fn.val = filename

def draw():
  global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT
  global levels,depth,pat_w,method,pat_fn
  glClear(GL_COLOR_BUFFER_BIT)
  PushButton("Load Pattern from", EVENT_SEL_PAT, 10, 156, 150, 16, 'Choose a file')
  pat_fn = String("Pat:", EVENT_SET_PAT, 10, 140, 300, 16, pat_fn.val, 200, 'Filename of the pattern image or "Random".')
  levels = Number('Levels:', EVENT_SET_LEVELS, 10, 110, 150, 20, levels.val, 2, 100, 'Number of levels')
  depth = Number('Depth:', EVENT_SET_DEPTH, 160, 110, 150, 20, depth.val, 1, 100, 'Level depth factor')
  pat_w = Number('Pattern Width:', EVENT_SET_PATW, 10, 90, 150, 20, pat_w.val, 1, 200, 'Repeating width of the background image')
  menu_str = "Method %t|Forward %x1|Backtrace %x2"
  Menu(menu_str, EVENT_SET_METHOD, 160, 90, 150, 20, method, "Stereogram generating method", menu_event)
  PushButton("IMAGE", EVENT_IMAGE, 10, 10, 100, 32)
  PushButton("ANIM", EVENT_ANIM, 110, 10, 100, 32)
  PushButton("EXIT", EVENT_EXIT, 210, 10, 100, 32)

def menu_event(evt, val):
  global method
  method = val
  print method

def event(evt, val):
  if (evt == QKEY and not val):
    Exit()
  elif (evt == IKEY and not val):
    generate()
  elif (evt == AKEY and not val):
    Window.ImageSelector (sel_pat_file, "Load Pattern", pat_fn.val)

def bevent(evt):
  global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT
  if (evt == EVENT_EXIT):
    Exit()
  elif (evt== EVENT_IMAGE):
    generate_image()
  elif (evt== EVENT_ANIM):
    generate_animation()
  elif (evt== EVENT_SEL_PAT):
    Window.ImageSelector (sel_pat_file, "Load Pattern", pat_fn.val)

Blender.Draw.Register(draw, event, bevent)




