Advertisement
R30

Banner_Art_v3.py

R30
May 16th, 2016
652
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.65 KB | None | 0 0
  1. # Smalecite Banner Filter
  2. # Generates Minecraft banner pixel art, based on a concept by docm77 and an algoritm by rsmalec
  3. # Inspiration and code snippits from @abrightmoore, @texelelf, and @Sethbling's filters
  4. #
  5. #
  6. # Filter by @R30hedron
  7.  
  8. import png
  9. import mcplatform
  10. from pymclevel import nbt, TAG_Compound, TAG_List, TAG_Int, TAG_Byte_Array, TAG_Short, TAG_Byte, TAG_String, TAG_Double, TAG_Float
  11. from pymclevel import TileEntity
  12.  
  13. import sys
  14. from pygame import display
  15.  
  16. if sys.platform == "win32":
  17. try:
  18. from win32 import win32gui
  19. from win32.lib import win32con
  20. except ImportError:
  21. import win32gui
  22. import win32con
  23.  
  24. lastDir = None
  25.  
  26. inputs = (
  27. ("@R30hedron", "label"),
  28. ("Banners Face", ("North (facing -Z)", "East (facing +X)", "South (facing +Z)", "West (facing -X)")),
  29. ("Attach to Wall?", True),
  30. ("Path and Filename", ("string","value=")),
  31. )
  32.  
  33. # color values pulled from http://minecraft.gamepedia.com/Wool
  34. colors = [
  35. ( 0, 25, 22, 22), # black
  36. ( 1, 150, 52, 48), # red
  37. ( 2, 53, 70, 27), # green
  38. ( 3, 79, 50, 31), # brown
  39. ( 4, 46, 56, 141), # blue
  40. ( 5, 126, 61, 181), # purple
  41. ( 6, 46, 110, 137), # cyan
  42. ( 7, 154, 161, 161), # light gray
  43. ( 8, 64, 64, 64), # gray
  44. ( 9, 208, 132, 153), # pink
  45. (10, 65, 174, 56), # lime
  46. (11, 177, 166, 39), # yellow
  47. (12, 107, 138, 201), # light blue
  48. (13, 179, 80, 188), # magenta
  49. (14, 219, 125, 62), # orange
  50. (15, 221, 221, 221), # white
  51. ]
  52.  
  53. #Because MCEdit doesn't standardize askOpenFile() for alternative file formats for Windows, I have to make my own. Go figure.
  54. #Code shamelessly pulled directly and slightly edited from MCEdit's source code; mcplatform module.
  55. def openPNGFile(title):
  56. global lastDir
  57. initialDir = lastDir
  58. if sys.platform == "win32":
  59. try:
  60. f = ('*.png')
  61. (filename, customfilter, flags) = win32gui.GetOpenFileNameW(
  62. hwndOwner=display.get_wm_info()['window'],
  63. InitialDir = initialDir,
  64. Flags=(win32con.OFN_EXPLORER
  65. | win32con.OFN_NOCHANGEDIR
  66. | win32con.OFN_FILEMUSTEXIST
  67. | win32con.OFN_LONGNAMES
  68. ),
  69. Title=title,
  70. Filter=f,
  71. )
  72. except Exception:
  73. pass
  74. else:
  75. return filename
  76. else:
  77. return mcplatform.askOpenFile(title, False, "png")
  78.  
  79. def getPixel(pixels, x, y): # @Sethbling
  80. idx = x*4
  81. return (pixels[y][idx], pixels[y][idx+1], pixels[y][idx+2], pixels[y][idx+3])
  82.  
  83. def transparent((r, g, b, a)): # @Sethbling
  84. return a < 1 #28
  85.  
  86. def closestWool((r, g, b, a)): # Adapted from @Sethbling
  87. closest = 255*255*3
  88. best = 15
  89. for (color, cr, cg, cb) in colors:
  90. (dr, dg, db) = (r-cr, g-cg, b-cb)
  91. dist = dr*dr+dg*dg+db*db
  92. if dist < closest:
  93. closest = dist
  94. best = color
  95. return best
  96.  
  97. # Calculate the closest wool colors and patterns for a set of values.
  98. # Normal:
  99. # 0 1
  100. # 2 3
  101. # 4 5
  102. def calcPattern(pixels):
  103. e = TileEntity.Create("Banner")
  104. #2
  105. e["Base"] = TAG_Int(pixels[2])
  106. e["Patterns"] = TAG_List()
  107. numPatterns = 0
  108.  
  109. #3
  110. if pixels[3] != pixels[2]:
  111. e["Patterns"].append(TAG_Compound())
  112. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[3])
  113. e["Patterns"][numPatterns]["Pattern"] = TAG_String("vhr")
  114. numPatterns = numPatterns + 1
  115.  
  116. #5
  117. if pixels[2] != pixels[3] and pixels[5] == pixels[4] or pixels[5] != pixels[3] and pixels[4] != pixels[2]:
  118. e["Patterns"].append(TAG_Compound())
  119. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
  120. e["Patterns"][numPatterns]["Pattern"] = TAG_String("bs")
  121. numPatterns = numPatterns + 1
  122. if pixels[5] != pixels[3] and pixels[5] != pixels[4] and pixels[4] == pixels[2]:
  123. e["Patterns"].append(TAG_Compound())
  124. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
  125. e["Patterns"][numPatterns]["Pattern"] = TAG_String("br")
  126. numPatterns = numPatterns + 1
  127. e["Patterns"].append(TAG_Compound())
  128. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
  129. e["Patterns"][numPatterns]["Pattern"] = TAG_String("br")
  130. numPatterns = numPatterns + 1
  131.  
  132. #4
  133. if pixels[4] != pixels[5] and pixels[4] != pixels[2]:
  134. e["Patterns"].append(TAG_Compound())
  135. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[4])
  136. e["Patterns"][numPatterns]["Pattern"] = TAG_String("bl")
  137. numPatterns = numPatterns + 1
  138.  
  139. #1
  140. if pixels[2] != pixels[3] and pixels[1] == pixels[0] or pixels[1] != pixels[3] and pixels[0] != pixels[2]:
  141. e["Patterns"].append(TAG_Compound())
  142. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
  143. e["Patterns"][numPatterns]["Pattern"] = TAG_String("ts")
  144. numPatterns = numPatterns + 1
  145. if pixels[1] != pixels[3] and pixels[1] != pixels[0] and pixels[0] == pixels[2]:
  146. e["Patterns"].append(TAG_Compound())
  147. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
  148. e["Patterns"][numPatterns]["Pattern"] = TAG_String("tr")
  149. numPatterns = numPatterns + 1
  150. e["Patterns"].append(TAG_Compound())
  151. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
  152. e["Patterns"][numPatterns]["Pattern"] = TAG_String("tr")
  153. numPatterns = numPatterns + 1
  154.  
  155. #0
  156. if pixels[0] != pixels[1] and pixels[0] != pixels[2]:
  157. e["Patterns"].append(TAG_Compound())
  158. e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[0])
  159. e["Patterns"][numPatterns]["Pattern"] = TAG_String("tl")
  160. numPatterns = numPatterns + 1
  161.  
  162. print numPatterns
  163. return e
  164.  
  165. def setBanner(level, (block, data), (x, y, z), EntityData):
  166. level.setBlockAt(x, y, z, block)
  167. level.setBlockDataAt(x, y, z, data)
  168. # Following code based on code from #abrightmoore's BlockCommand Filter
  169. chunk = level.getChunk(x/16, z/16)
  170. TileEntity.setpos(EntityData, (x, y, z))
  171. chunk.TileEntities.append(EntityData)
  172.  
  173. # Assign the block and orientation for each banner
  174. def assignOrientation(face, attach):
  175. if face == "North (facing -Z)":
  176. if attach: orient = (177, 2)
  177. else: orient = (176, 8)
  178. elif face == "East (facing +X)":
  179. if attach: orient = (177, 5)
  180. else: orient = (176, 12)
  181. elif face == "South (facing +Z)":
  182. if attach: orient = (177, 3)
  183. else: orient = (176, 0)
  184. elif face == "West (facing -X)":
  185. if attach: orient = (177, 4)
  186. else: orient = (176, 4)
  187. else:
  188. raise Exception('"Banner Facing" Not a direction')
  189.  
  190. return orient
  191.  
  192. # Assign the direction that we will iterate through the bounding box (left to right; top to bottom from perspective of the FACE option)
  193. def assignIterDirection(face, attach, box):
  194. direction = []
  195. # If the banners are not attached to a wall, lower the bounding box by one to accomodate.
  196. if attach:
  197. adjust = 0
  198. else:
  199. adjust = -1
  200. if face == "North (facing -Z)":
  201. for iterX in xrange(box.maxx - 1, box.minx - 1, -1):
  202. for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
  203. direction.append((iterX, iterY, box.minz))
  204. elif face == "East (facing +X)":
  205. for iterZ in xrange(box.maxz - 1, box.minz - 1, -1):
  206. for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
  207. direction.append((box.minx, iterY, iterZ))
  208. elif face == "South (facing +Z)":
  209. for iterX in xrange(box.minx, box.maxx):
  210. for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
  211. direction.append((iterX, iterY, box.minz))
  212. elif face == "West (facing -X)":
  213. for iterZ in xrange(box.minz, box.maxz):
  214. for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
  215. direction.append((box.minx, iterY, iterZ))
  216. else:
  217. raise Exception('"Banner Facing" Not a direction')
  218.  
  219. return direction
  220.  
  221. def perform(level, box, options):
  222. AIR = (0,0)
  223. FACE = options["Banners Face"]
  224. ATTACH = options["Attach to Wall?"]
  225.  
  226. print "Smalecite Banner Art Filter by R30hedron"
  227.  
  228. # perform a bounding box check.
  229. if FACE == "North (facing -Z)" or FACE == "South (facing +Z)":
  230. if box.width == 1:
  231. raise Exception("Bounding Box NG; change facing direction to East or West.")
  232. if box.length != 1:
  233. raise Exception("Bounding Box NG; adjust selection to 1 block width.")
  234. elif FACE == "East (facing +X)" or FACE == "West (facing -X)":
  235. if box.length == 1:
  236. raise Exception("Bounding Box NG; change facing direction to North or South.")
  237. if box.width != 1:
  238. raise Exception("Bounding Box NG; adjust selection to 1 block width.")
  239. else:
  240. raise Exception('"Banner Facing" Not a Direction')
  241.  
  242. # get .png from user
  243. filename = options["Path and Filename"]
  244. filename = filename.strip()
  245. if filename == "":
  246. filename = openPNGFile("Select a .png file...")
  247. if filename == None:
  248. raise Exception("No file name provided.")
  249.  
  250. f = open(filename, "rb") # @abrightmoore
  251. data = f.read() # @abrightmoore
  252. f.close() # @abrightmoore
  253.  
  254. reader = png.Reader(bytes=data) # @Sethbling
  255. (width, height, pixels, metadata) = reader.asRGBA8() # @Sethbling
  256. pixels = list(pixels) # @Sethbling
  257.  
  258. # Test the height and width of the png compared to the bounding box. If it doesn't match, toss an exception.
  259. if height % 2 != 1 or width % 2 != 0:
  260. raise Exception("The .png file is not compatible! Make sure the width is even, and the height is odd.")
  261.  
  262. if width == 2 * max(box.width, box.length) and height == 2 * (box.height - 1) + 1:
  263. print ".png OK"
  264. else:
  265. raise Exception("The .png file does not match the size of the bounding box. Make sure that the width of the bounding box is " + str(width//2) + " and the height is " + str(height//2 + 1) + ".")
  266.  
  267. # Create "cells" for each banner from the png file, while converting the pixels in each cell to wool colors
  268. cells = []
  269. for iterW in xrange(0, width, 2):
  270. for iterH in xrange(1, height, 2):
  271. newArray = []
  272. # 0 1
  273. # 2 3
  274. # 4 5
  275. newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH - 1))) # newArray[0]
  276. newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH - 1))) # newArray[1]
  277. newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH + 0))) # newArray[2]
  278. newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH + 0))) # newArray[3]
  279. newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH + 1))) # newArray[4]
  280. newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH + 1))) # newArray[5]
  281. cells.append(newArray)
  282.  
  283. patterns = []
  284. for iter in cells:
  285. patterns.append(calcPattern(iter))
  286.  
  287. orient = assignOrientation(FACE, ATTACH)
  288. direction = assignIterDirection(FACE, ATTACH, box)
  289.  
  290. print len(direction), len(cells), len(patterns)
  291.  
  292. for index, item in enumerate(direction):
  293. thisBlock = (level.blockAt(item[0], item[1], item[2]), level.blockDataAt(item[0], item[1], item[2]))
  294. if thisBlock == AIR:
  295. #print "banner set"
  296. setBanner(level, orient, item, patterns[index])
  297.  
  298. level.markDirtyBox(box)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement