Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Smalecite Banner Filter
- # Generates Minecraft banner pixel art, based on a concept by docm77 and an algoritm by rsmalec
- # Inspiration and code snippits from @abrightmoore, @texelelf, and @Sethbling's filters
- #
- #
- # Filter by @R30hedron
- import png
- import mcplatform
- from pymclevel import nbt, TAG_Compound, TAG_List, TAG_Int, TAG_Byte_Array, TAG_Short, TAG_Byte, TAG_String, TAG_Double, TAG_Float
- from pymclevel import TileEntity
- import sys
- from pygame import display
- if sys.platform == "win32":
- try:
- from win32 import win32gui
- from win32.lib import win32con
- except ImportError:
- import win32gui
- import win32con
- lastDir = None
- inputs = (
- ("@R30hedron", "label"),
- ("Banners Face", ("North (facing -Z)", "East (facing +X)", "South (facing +Z)", "West (facing -X)")),
- ("Attach to Wall?", True),
- ("Path and Filename", ("string","value=")),
- )
- # color values pulled from http://minecraft.gamepedia.com/Wool
- colors = [
- ( 0, 25, 22, 22), # black
- ( 1, 150, 52, 48), # red
- ( 2, 53, 70, 27), # green
- ( 3, 79, 50, 31), # brown
- ( 4, 46, 56, 141), # blue
- ( 5, 126, 61, 181), # purple
- ( 6, 46, 110, 137), # cyan
- ( 7, 154, 161, 161), # light gray
- ( 8, 64, 64, 64), # gray
- ( 9, 208, 132, 153), # pink
- (10, 65, 174, 56), # lime
- (11, 177, 166, 39), # yellow
- (12, 107, 138, 201), # light blue
- (13, 179, 80, 188), # magenta
- (14, 219, 125, 62), # orange
- (15, 221, 221, 221), # white
- ]
- #Because MCEdit doesn't standardize askOpenFile() for alternative file formats for Windows, I have to make my own. Go figure.
- #Code shamelessly pulled directly and slightly edited from MCEdit's source code; mcplatform module.
- def openPNGFile(title):
- global lastDir
- initialDir = lastDir
- if sys.platform == "win32":
- try:
- f = ('*.png')
- (filename, customfilter, flags) = win32gui.GetOpenFileNameW(
- hwndOwner=display.get_wm_info()['window'],
- InitialDir = initialDir,
- Flags=(win32con.OFN_EXPLORER
- | win32con.OFN_NOCHANGEDIR
- | win32con.OFN_FILEMUSTEXIST
- | win32con.OFN_LONGNAMES
- ),
- Title=title,
- Filter=f,
- )
- except Exception:
- pass
- else:
- return filename
- else:
- return mcplatform.askOpenFile(title, False, "png")
- def getPixel(pixels, x, y): # @Sethbling
- idx = x*4
- return (pixels[y][idx], pixels[y][idx+1], pixels[y][idx+2], pixels[y][idx+3])
- def transparent((r, g, b, a)): # @Sethbling
- return a < 1 #28
- def closestWool((r, g, b, a)): # Adapted from @Sethbling
- closest = 255*255*3
- best = 15
- for (color, cr, cg, cb) in colors:
- (dr, dg, db) = (r-cr, g-cg, b-cb)
- dist = dr*dr+dg*dg+db*db
- if dist < closest:
- closest = dist
- best = color
- return best
- # Calculate the closest wool colors and patterns for a set of values.
- # Normal:
- # 0 1
- # 2 3
- # 4 5
- def calcPattern(pixels):
- e = TileEntity.Create("Banner")
- #2
- e["Base"] = TAG_Int(pixels[2])
- e["Patterns"] = TAG_List()
- numPatterns = 0
- #3
- if pixels[3] != pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[3])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("vhr")
- numPatterns = numPatterns + 1
- #5
- if pixels[2] != pixels[3] and pixels[5] == pixels[4] or pixels[5] != pixels[3] and pixels[4] != pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("bs")
- numPatterns = numPatterns + 1
- if pixels[5] != pixels[3] and pixels[5] != pixels[4] and pixels[4] == pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("br")
- numPatterns = numPatterns + 1
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[5])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("br")
- numPatterns = numPatterns + 1
- #4
- if pixels[4] != pixels[5] and pixels[4] != pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[4])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("bl")
- numPatterns = numPatterns + 1
- #1
- if pixels[2] != pixels[3] and pixels[1] == pixels[0] or pixels[1] != pixels[3] and pixels[0] != pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("ts")
- numPatterns = numPatterns + 1
- if pixels[1] != pixels[3] and pixels[1] != pixels[0] and pixels[0] == pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("tr")
- numPatterns = numPatterns + 1
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[1])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("tr")
- numPatterns = numPatterns + 1
- #0
- if pixels[0] != pixels[1] and pixels[0] != pixels[2]:
- e["Patterns"].append(TAG_Compound())
- e["Patterns"][numPatterns]["Color"] = TAG_Int(pixels[0])
- e["Patterns"][numPatterns]["Pattern"] = TAG_String("tl")
- numPatterns = numPatterns + 1
- print numPatterns
- return e
- def setBanner(level, (block, data), (x, y, z), EntityData):
- level.setBlockAt(x, y, z, block)
- level.setBlockDataAt(x, y, z, data)
- # Following code based on code from #abrightmoore's BlockCommand Filter
- chunk = level.getChunk(x/16, z/16)
- TileEntity.setpos(EntityData, (x, y, z))
- chunk.TileEntities.append(EntityData)
- # Assign the block and orientation for each banner
- def assignOrientation(face, attach):
- if face == "North (facing -Z)":
- if attach: orient = (177, 2)
- else: orient = (176, 8)
- elif face == "East (facing +X)":
- if attach: orient = (177, 5)
- else: orient = (176, 12)
- elif face == "South (facing +Z)":
- if attach: orient = (177, 3)
- else: orient = (176, 0)
- elif face == "West (facing -X)":
- if attach: orient = (177, 4)
- else: orient = (176, 4)
- else:
- raise Exception('"Banner Facing" Not a direction')
- return orient
- # Assign the direction that we will iterate through the bounding box (left to right; top to bottom from perspective of the FACE option)
- def assignIterDirection(face, attach, box):
- direction = []
- # If the banners are not attached to a wall, lower the bounding box by one to accomodate.
- if attach:
- adjust = 0
- else:
- adjust = -1
- if face == "North (facing -Z)":
- for iterX in xrange(box.maxx - 1, box.minx - 1, -1):
- for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
- direction.append((iterX, iterY, box.minz))
- elif face == "East (facing +X)":
- for iterZ in xrange(box.maxz - 1, box.minz - 1, -1):
- for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
- direction.append((box.minx, iterY, iterZ))
- elif face == "South (facing +Z)":
- for iterX in xrange(box.minx, box.maxx):
- for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
- direction.append((iterX, iterY, box.minz))
- elif face == "West (facing -X)":
- for iterZ in xrange(box.minz, box.maxz):
- for iterY in xrange(box.maxy - 1 + adjust, box.miny + adjust, -1):
- direction.append((box.minx, iterY, iterZ))
- else:
- raise Exception('"Banner Facing" Not a direction')
- return direction
- def perform(level, box, options):
- AIR = (0,0)
- FACE = options["Banners Face"]
- ATTACH = options["Attach to Wall?"]
- print "Smalecite Banner Art Filter by R30hedron"
- # perform a bounding box check.
- if FACE == "North (facing -Z)" or FACE == "South (facing +Z)":
- if box.width == 1:
- raise Exception("Bounding Box NG; change facing direction to East or West.")
- if box.length != 1:
- raise Exception("Bounding Box NG; adjust selection to 1 block width.")
- elif FACE == "East (facing +X)" or FACE == "West (facing -X)":
- if box.length == 1:
- raise Exception("Bounding Box NG; change facing direction to North or South.")
- if box.width != 1:
- raise Exception("Bounding Box NG; adjust selection to 1 block width.")
- else:
- raise Exception('"Banner Facing" Not a Direction')
- # get .png from user
- filename = options["Path and Filename"]
- filename = filename.strip()
- if filename == "":
- filename = openPNGFile("Select a .png file...")
- if filename == None:
- raise Exception("No file name provided.")
- f = open(filename, "rb") # @abrightmoore
- data = f.read() # @abrightmoore
- f.close() # @abrightmoore
- reader = png.Reader(bytes=data) # @Sethbling
- (width, height, pixels, metadata) = reader.asRGBA8() # @Sethbling
- pixels = list(pixels) # @Sethbling
- # Test the height and width of the png compared to the bounding box. If it doesn't match, toss an exception.
- if height % 2 != 1 or width % 2 != 0:
- raise Exception("The .png file is not compatible! Make sure the width is even, and the height is odd.")
- if width == 2 * max(box.width, box.length) and height == 2 * (box.height - 1) + 1:
- print ".png OK"
- else:
- 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) + ".")
- # Create "cells" for each banner from the png file, while converting the pixels in each cell to wool colors
- cells = []
- for iterW in xrange(0, width, 2):
- for iterH in xrange(1, height, 2):
- newArray = []
- # 0 1
- # 2 3
- # 4 5
- newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH - 1))) # newArray[0]
- newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH - 1))) # newArray[1]
- newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH + 0))) # newArray[2]
- newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH + 0))) # newArray[3]
- newArray.append(closestWool(getPixel(pixels, iterW + 0, iterH + 1))) # newArray[4]
- newArray.append(closestWool(getPixel(pixels, iterW + 1, iterH + 1))) # newArray[5]
- cells.append(newArray)
- patterns = []
- for iter in cells:
- patterns.append(calcPattern(iter))
- orient = assignOrientation(FACE, ATTACH)
- direction = assignIterDirection(FACE, ATTACH, box)
- print len(direction), len(cells), len(patterns)
- for index, item in enumerate(direction):
- thisBlock = (level.blockAt(item[0], item[1], item[2]), level.blockDataAt(item[0], item[1], item[2]))
- if thisBlock == AIR:
- #print "banner set"
- setBanner(level, orient, item, patterns[index])
- level.markDirtyBox(box)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement