07-30-2025, 07:14 PM
Hi!
Sure:
I tried to put a lots of comment
Thanx
Csaba
Sure:
Code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
import gi
import random
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('Gegl', '0.4')
from gi.repository import Gegl
from gi.repository import GLib
from gi.repository import Gio
def N_(message): return message
def _(message): return GLib.dgettext(None, message)
class TilesetVariants (Gimp.PlugIn):
def do_query_procedures(self):
return [ "plug-in-tileset-variants" ]
def do_create_procedure(self, name):
procedure = Gimp.ImageProcedure.new(self, name,
Gimp.PDBProcType.PLUGIN,
self.run, None)
procedure.set_image_types("*")
procedure.set_sensitivity_mask (Gimp.ProcedureSensitivityMask.DRAWABLE)
procedure.set_menu_label(_("tileset"))
procedure.add_menu_path('<Image>/Filters/Tileset/')
procedure.set_documentation(_("Plug-in example in Python 3"),
_("Plug-in example in Python 3"),
name)
procedure.set_attribution("ignis", "ignis", "2025")
return procedure
def run(self, procedure, run_mode, image, drawables, config, run_data):
Gimp.message("running")
try:
tileset_variants(image,drawables)
except Exception as e:
Gimp.message(f"error: {e}")
# do what you want to do, then, in case of success, return:
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
#apply normal filter to the layer
def apply_normalMap(layer):
filter = Gimp.DrawableFilter.new(layer, "gegl:normal-map", "Normal Map")
filter.set_blend_mode(Gimp.LayerMode.REPLACE)
filter.set_opacity(1.0)
config = filter.get_config()
config.set_property('scale', 100)
filter.update()
layer.append_filter(filter)
#apply hsv noise to the filter
def apply_noise(layer):
filter = Gimp.DrawableFilter.new(layer, "gegl:noise-hsv", "Add HSV Noise")
filter.set_blend_mode(Gimp.LayerMode.REPLACE)
filter.set_opacity(1.0)
#config = filter.get_config()
#config.set_property('Dulling', 2)
#config.set_property('Hue', 3)
#config.set_property('Saturation', 0.04)
#config.set_property('Value', 0.04)
filter.update()
layer.append_filter(filter)
#create a layer, and fill up with grayscale noise between min and max value
def generate_noise(image,min,max,w,h):
layers = []
for i in range(4):
layer = Gimp.Layer.new(image, "Noise_" + str(i), w, h,
Gimp.ImageType.RGBA_IMAGE, 100.0, Gimp.LayerMode.NORMAL)
image.insert_layer(layer, None, -1)
color_format = "#{:02x}{:02x}{:02x}"
colors = []
for c in range(min,max+1):
colors.append(Gegl.Color.new(color_format.format(c,c,c)))
for y in range(h):
for x in range(w):
cp = random.randint(0,len(colors)-1)
#pixels.extend([gray, gray, gray, 255]) # R, G, B, A
layer.set_pixel(x,y,colors[cp])
layer.merge_shadow(True)
layer.update(0, 0, w, h)
layer.set_offsets(i * w, 0)
layer.set_mode(Gimp.LayerMode.OVERLAY)
layer.set_visible(False)
layers.append(layer)
return layers
#rename the layer: workaround: remove layer, copy layer, rename new layer, add to image the new layer
def rename_layer(image,layer,name):
if not layer:
return None
parent = layer.get_parent()
position = image.get_item_position(layer)
image.remove_layer(layer)
new_layer = layer.copy()
new_layer.set_name(name)
image.insert_layer(new_layer, parent, position)
return new_layer
#create rotated images and offset is
def generate_variants(image,layer,width):
if layer == None:
return [None]*4
variants = [layer]
for angle in range(1,4):
rotated = layer.copy()
rotated.set_name(layer.get_name()+"_"+str(angle))
image.insert_layer(rotated, None, -1)
rotate_angle = Gimp.RotationType.DEGREES90
if angle == 2:
rotate_angle =Gimp.RotationType.DEGREES180
if angle == 3:
rotate_angle =Gimp.RotationType.DEGREES270
rotated.transform_rotate_simple(rotate_angle,True,0,0)
floating=image.get_selected_layers()
# many times get an error, cant anchor not floating image
# if I leave it, one or two image not moved and/or not rotated
try:
Gimp.floating_sel_anchor(floating[0])
except Exception as e:
pass
rotated.set_offsets(angle * width, 0)
variants.append(rotated)
return variants
# get the mask layer, select the black color, and fill the selection with neutral gray on the noise layer
def mask_noise(componed_layers,image):
Gimp.context_set_background(Gegl.Color.new("#808080"))
for i in range(4):
mask = componed_layers["Mask"][i]
if mask!=None:
image.select_color(Gimp.ChannelOps.REPLACE, mask, Gegl.Color.new("#000000"))
noise_layer = componed_layers["Noise"][i]
image.active_layer = noise_layer
noise_layer.edit_fill(Gimp.FillType.BACKGROUND)
# to be sure noting is selected
selection = image.get_selection()
selection.none(image)
# get the noise layer, the normal layer, and the noise addon, and merged together
def merge_noise(componed_layers,image):
#merge noise es tarsai:
for i in range(4):
noise = componed_layers["Noise"][i]
normal = componed_layers["Normal"][i]
addon = componed_layers["Addon"][i]
noise.set_visible(True)
normal.set_visible(True)
image.reorder_item(normal,None,-1)
image.reorder_item(noise, None,-1)
name = normal.get_name()
Gimp.message(normal.get_name())
if addon!=None:
addon.set_visible(True)
image.reorder_item(addon, None,-1)
image.active_layer = normal
normal = image.merge_visible_layers(Gimp.MergeType.EXPAND_AS_NECESSARY)
#need to rename the last layer
Gimp.message(normal.get_name())
normal=rename_layer(image,normal,name)
componed_layers["Normal"][i]=normal
normal.set_visible(False)
# generate noise for the Color image
# get the mask, select the black, invert the selection, and generate the noise into the selection on the color layer
def color_noise(componed_layers,image):
for i in range(4):
color = componed_layers["Color"][i]
mask = componed_layers["ColorMask"][i]
color.set_visible(True)
image.reorder_item(color,None,-1)
apply = color
if mask!=None:
mask.set_visible(True)
image.reorder_item(mask, None,-1)
image.select_color(Gimp.ChannelOps.REPLACE, mask, Gegl.Color.new("#000000"))
selection = image.get_selection()
selection.invert(image)
apply = selection
image.active_layer = color
apply_noise(apply)
selection = image.get_selection()
if selection:
selection.none(image)
color.set_visible(False)
# main function
def tileset_variants(image, drawable):
Gimp.message("variant running")
# get the file name to the export
orig_path = image.get_file().get_path()
basename = os.path.basename(orig_path)
dirname = os.path.dirname(orig_path)
new_name = os.path.splitext(basename)[0] + ".ora"
output_path = os.path.join(dirname, new_name)
Gimp.message("before")
image.undo_group_start()
#set up dedicated layers
color = None
heightmap = None
mask = None
normal = None
addon = None
colormask = None
#Extract layers
for layer in image.get_layers():
layer.set_visible(False)
# Color: visible texture
if layer.get_name() == "Color":
color = layer
# Heightmap
if layer.get_name() == "Heightmap":
heightmap = layer
# Mask, where dont need noise for heightmap
if layer.get_name() == "Noisemask":
mask = layer
# layer added to the heightmap aka details
if layer.get_name() == "Heightaddon":
addon = layer
# color mask, where deont need noise for color layer
if layer.get_name() == "Colormask":
colormask = layer
width = image.get_width()
height = image.get_height()
# resize image
image.resize(width * 4, height,0,0)
# duplicate heightmap, rename to normal
normal = heightmap.copy()
normal.set_name("Normal")
image.insert_layer(normal, None, -1)
#collect the layers
all_layers = {"Color":color, "Heightmap":heightmap, "Normal":normal,"Mask":mask,"Addon":addon,"ColorMask":colormask}
# rotate and shift, create dict for layers
componed_layers={}
for name,layer in all_layers.items():
variants = generate_variants(image,layer,width)
componed_layers[name]=variants
# generate noise layer
componed_layers["Noise"]=generate_noise(image,123,133,width,height)
# create noise
mask_noise(componed_layers,image)
#create heightmap for normal (apply noise, mask, details onto the copied heightmap)
merge_noise(componed_layers,image)
#apply normal map filter
for i in range(4):
normal = componed_layers["Normal"][i]
apply_normalMap(normal)
#apply color noise
color_noise(componed_layers,image)
#merge down each group
Gimp.message("flattening")
for name,layer in componed_layers.items():
Gimp.message("group: "+name)
needMerge = False
for l in layer:
if l:
Gimp.message("layer: "+l.get_name())
l.set_visible(True)
image.active_layer = l
needMerge = True
if needMerge:
merged = image.merge_visible_layers(Gimp.MergeType.EXPAND_AS_NECESSARY)
if merged:
Gimp.message("rename: "+merged.get_name())
merged=rename_layer(image,merged,name)
merged.set_visible(False)
image.active_layer = merged
#try to export as openraster
Gimp.file_save(Gimp.RunMode.INTERACTIVE,image, Gio.File.new_for_path(output_path),None)
image.undo_group_end()
Gimp.message("End")
Gimp.main(TilesetVariants.__gtype__, sys.argv)
Thanx
Csaba