Skip to content

Instantly share code, notes, and snippets.

@onnowhere
Last active May 8, 2022 11:37
Show Gist options
  • Save onnowhere/5b717daeba899ff4dc606add12d6b2b5 to your computer and use it in GitHub Desktop.
Save onnowhere/5b717daeba899ff4dc606add12d6b2b5 to your computer and use it in GitHub Desktop.
[Image to Particles] Quick tool that generates a function that displays particles for an input image using local coordinates. Drop images in the `images` folder in the same directory as the script. You'll also need to `pip install opencv-python`. Edit generation options at the bottom of the file.
import cv2
import os
class ImageToParticle:
def __init__(self, image_file, resolution=(40, 40), scale=0.25, max_size=5, replace_transparent=[], animation=0, commands=[], show_display=False):
self.image_file = image_file # Source file
self.resolution = resolution # Shrinks image to fit within x,y dimensions without changing aspect ratio (1 pixel = 1 particle)
self.scale = scale # Scale the size of image displayed in game
self.max_size = max_size # Maximum particle size, alpha scales this if replace_transparent is not defined
self.replace_transparent = replace_transparent # RGB value to replace 0 alpha with
self.animation = animation # Animation type, 0 - Lines from bottom, 1 - Spiral from center
self.commands = commands # Additional commands to add to start of each function
self.show_display = show_display # Display resized image for debugging
def create_path(self, filepath):
if not os.path.exists(filepath):
try:
os.makedirs(filepath)
except OSError as exc: # Guard against race condition
if exc.errno != errno.EEXIST:
raise
def create_file(self, filename, contents):
self.create_path(os.path.dirname(filename))
with open(filename, 'w', encoding='utf-8') as f:
f.writelines(contents)
def create_empty_function_folder(self, paths):
self.create_path(paths["functions"])
def get_color(self, pixel):
if len(pixel) > 3:
if pixel[3] == 0.0 and len(self.replace_transparent) == 3:
# Replace full transparency with value
return "{0} {1} {2} {3}".format(self.replace_transparent[0], self.replace_transparent[1], self.replace_transparent[2], self.max_size)
elif len(self.replace_transparent) != 3:
return "{0} {1} {2} {3}".format(round(pixel[0]/256, 3), round(pixel[1]/256, 3), round(pixel[2]/256, 3), round(self.max_size * pixel[3]/256, 3))
return "{0} {1} {2} {3}".format(round(pixel[0]/256, 3), round(pixel[1]/256, 3), round(pixel[2]/256, 3), self.max_size)
def get_position(self, row, col):
return "^{0} ^{1} ^0".format(round(row*self.scale, 3), round(col*self.scale, 3))
def generate_particle_command(self, image, row, col):
pixel = image[image.shape[0] - 1 - col, image.shape[1] - 1 - row]
if len(pixel) > 3:
color = self.get_color([pixel[2], pixel[1], pixel[0], pixel[3]])
else:
color = self.get_color([pixel[2], pixel[1], pixel[0]])
position = self.get_position(row - image.shape[1]/2, col - image.shape[0]/2)
return 'particle dust {0} {1} 0 0 0 0 0 force'.format(color, position)
def generate(self):
image_prefix = "image_"
datapack_name = image_prefix + os.path.basename(os.path.splitext(self.image_file)[0]).lower().replace(" ","_")
paths = {
"functions": "particle_images"
}
self.create_empty_function_folder(paths)
image = cv2.imread(self.image_file, cv2.IMREAD_UNCHANGED)
function_contents = []
image_size = [image.shape[0], image.shape[1]]
if image_size[0] > self.resolution[0]:
scale = self.resolution[0] / image_size[0]
image_size[1] = int(image_size[1] * scale)
image_size[0] = self.resolution[0]
if image_size[1] > self.resolution[1]:
scale = self.resolution[1] / image_size[1]
image_size[0] = int(image_size[0] * scale)
image_size[1] = self.resolution[1]
image = cv2.resize(image, (image_size[1], image_size[0]))
output = []
output += self.commands
if self.animation == 0:
# Lines from bottom
for col in range(image.shape[0]):
for row in range(image.shape[1]):
output.append(self.generate_particle_command(image, row, col))
elif self.animation == 1:
# Spiral from center
offset = [0, 0]
direction = [0, -1]
while abs(offset[0]) < image.shape[0]//2 or abs(offset[1]) < image.shape[1]//2:
if offset[0] == offset[1] or (offset[0] < 0 and offset[0] == -offset[1]) or (offset[0] > 0 and offset[0] == 1 - offset[1]):
direction = [-direction[1], direction[0]]
offset[0] += direction[0]
offset[1] += direction[1]
if abs(offset[0]) < image.shape[0]//2 and abs(offset[1]) < image.shape[1]//2:
position = [offset[0] + image.shape[0]//2, offset[1] + image.shape[1]//2]
output.append(self.generate_particle_command(image, position[1], position[0]))
output = '\n'.join(output)
function_contents.append(output)
if self.show_display:
cv2.imshow('image',image)
cv2.waitKey(0)
self.create_file(os.path.join(paths["functions"], "{0}.mcfunction".format(datapack_name)), '\n'.join(function_contents))
if __name__ == '__main__':
for file in os.listdir("images"):
# With 0 alpha replacement, particles won't scale
#image = ImageToParticle(os.path.join("images", file), resolution=(40, 40), scale=0.8, max_size=5, replace_transparent=[1.0, 1.0, 1.0], animation=1, commands=[], show_display=False)
# Without 0 alpha replacement, scales particle size by alpha
image = ImageToParticle(os.path.join("images", file), resolution=(40, 40), scale=0.8, max_size=5, animation=1, commands=[], show_display=False)
image.generate()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment