-
Notifications
You must be signed in to change notification settings - Fork 104
Thick Rings Texture Python Script
Max Hyper edited this page Jun 3, 2025
·
3 revisions
The following python script can be executed to generate the textures for the thick rings from a base texture.
An explanation on how to use it can be found here.
import tkinter as tk
from tkinter import filedialog
from enum import Enum
import numpy as np
import cv2
import sys
class Direction(object):
def __init__(self, name, xOffset, zOffset, axis, rotateClockWiseIndex):
self.name = name
self._xOffset = xOffset
self._zOffset = zOffset
self._axis = axis
self._rotateClockWiseIndex = rotateClockWiseIndex
@property
def xOffset(self):
return self._xOffset
@property
def zOffset(self):
return self._zOffset
@property
def axis(self):
return self._axis
@property
def rotateClockWiseIndex(self):
return self._rotateClockWiseIndex
def rotateY(dir):
return Horizontals[dir.rotateClockWiseIndex]
Horizontals = [Direction("SOUTH", 0, 1, "Z", 1), Direction("WEST", -1, 0, "X", 2), Direction("NORTH", 0, -1, "Z", 3), Direction("EAST", 1, 0, "X", 0)]
def newImage (width, height):
return np.zeros((width,height,3), np.uint8)
def setPixel (image, x, y, color):
w, h, channels = image.shape
if x >= 0 and x < w and y >= 0 and y < h:
image[x,y] = color
def blit (imageTo, imageFrom, offX, offY, rotCW90):
w, h, channels = imageFrom.shape
rotation = rotCW90 & 3
if rotation == 0:
for y in range(h):
for x in range(w):
setPixel(imageTo, x + offX, y + offY, imageFrom[x,y])
return
if rotation == 1:
for y in range(h):
for x in range(w):
destX = h - y - 1
setPixel(imageTo, destX + offX, x + offY, imageFrom[x,y])
return
if rotation == 2:
for y in range(h):
for x in range(w):
destX = w - x - 1
destY = h - y - 1
setPixel(imageTo, destX + offX, destY + offY, imageFrom[x,y])
return
if rotation == 3:
for y in range(h):
for x in range(w):
destY = w - x - 1
setPixel(imageTo, y + offX, destY + offY, imageFrom[x,y])
return
def createBarklessAntecedent (baseBuffer):
baseW, baseH, baseChannels = baseBuffer.shape
antecedent = newImage(baseW,baseH)
scale = int(baseW / 16)
blit(antecedent, baseBuffer, 3*scale, 3*scale, 1)
blit(antecedent, baseBuffer, -3*scale, 3*scale, 1)
blit(antecedent, baseBuffer, 3*scale, -3*scale, 1)
blit(antecedent, baseBuffer, -3*scale, -3*scale, 1)
ringStrip = newImage(6*scale,scale)
blit(ringStrip, baseBuffer, -5*scale, -3*scale, 0)
blit(antecedent, ringStrip, 0*scale, 2*scale, -1)
blit(antecedent, ringStrip, 15*scale, 8*scale, 1)
blit(ringStrip, baseBuffer, -5*scale, -12*scale, 0)
blit(antecedent, ringStrip, 0*scale, 8*scale, 1)
blit(antecedent, ringStrip, 15*scale, 2*scale, -1)
ringStrip = newImage(scale,6*scale)
blit(ringStrip, baseBuffer, -3*scale, -5*scale, 0)
blit(antecedent, ringStrip, 2*scale, 0*scale, -1)
blit(antecedent, ringStrip, 8*scale, 15*scale, 1)
blit(ringStrip, baseBuffer, -12*scale, -5*scale, 0)
blit(antecedent, ringStrip, 8*scale, 0*scale, 1)
blit(antecedent, ringStrip, 2*scale, 15*scale, -1)
center = newImage(14*scale, 14*scale)
blit(center, baseBuffer, -1*scale, -1*scale, 0)
blit(antecedent, center, 1*scale, 1*scale, 0)
return antecedent
def createMajorTexture(baseBuffer):
baseW, baseH, baseChannels = baseBuffer.shape
scale = int(baseW / 16)
majorW = 3*baseW
majorH = 3*baseH
antec_image = createBarklessAntecedent(baseBuffer)
major_image = newImage(majorW,majorH)
corners = [0]*4
edges = [0]*4
for i in range(4):
corners[i] = newImage(6*scale, 6*scale)
edges[i] = newImage(4*scale, 6*scale)
blit(corners[i], antec_image, 0, 0, i)
blit(edges[i], antec_image, -6*scale, 0, i)
centerX = 24
centerY = 24
for nesting in range(3):
edge = 2
imgSel = 0
for dir in Horizontals:
ovr = Direction.rotateY(dir)
offX = dir.xOffset
offY = dir.zOffset
compX = (-6 if offX == 1 else 0) + (-2 if dir.axis == "Z" else 0)
compY = (-6 if offY == 1 else 0) + (-2 if dir.axis == "X" else 0)
startX = offX * (14 + nesting * 6)
startY = offY * (14 + nesting * 6)
for way in [-1, 1]:
for i in range(4+nesting):
rowX = ovr.xOffset * i * way * 4
rowY = ovr.zOffset * i * way * 4
realX = centerX + startX + compX + rowX
realY = centerY + startY + compY + rowY
blit(major_image, edges[((imgSel * 13402141) >> 1) & 3], realX * scale, realY * scale, edge)
imgSel = imgSel+1
edge = edge+1
cornerX = [-1, 1, 1, -1]
cornerY = [-1, -1, 1, 1]
blit(major_image, antec_image, 16*scale, 16*scale, 0)
for nesting in [1,2,3]:
for corner in range(4):
corner_pixels = corners[(corner + nesting) & 0x3 ]
cX = cornerX[corner]
cY = cornerY[corner]
offX = cX * 6 * nesting + cX * 5
offY = cY * 6 * nesting + cY * 5
realX = 16 + 5 + offX
realY = 16 + 5 + offY
blit(major_image, corner_pixels, realX * scale, realY * scale, corner)
cornersW = scale
cornersH = scale
edgesW = 14*scale
edgesH = scale
for i in range(4):
corners[i] = newImage(cornersW, cornersH)
edges[i] = newImage(edgesW, edgesH)
blit(corners[i], baseBuffer, 0, 0, i)
blit(edges[i], baseBuffer, -1*scale, 0, i)
pixSel = 0
for row in range(4):
edge = edges[((pixSel * 13402141) >> 1) & 3]
pixSel = pixSel+1
span = edgesW
blit(major_image, edge, (1 + row * span) * scale, 0, 0)
blit(major_image, edge, (majorW - edgesH) * scale, (1 + row * span) * scale, 1)
blit(major_image, edge, (majorW - 1 - span - row * span) * scale, (majorH - edgesH) * scale, 2)
blit(major_image, edge, 0, (majorH - 1 - edgesW - row * span) * scale, 3)
return major_image
tk.Tk().withdraw()
if len(sys.argv) > 1:
paths = sys.argv
del paths[0]
else:
paths = filedialog.askopenfilenames()
for path in paths:
print("Loading file {}".format(path))
img = cv2.imread(path)
if img is None:
print("Unable to load file {}".format(path))
exit()
print("Generating texture")
thick_img = createMajorTexture(img)
path_split = path.split('.')
thick_path = path_split[0] + "_thick." + path_split[1]
cv2.imwrite(thick_path, thick_img)
print("Successfully created texture {}".format(thick_path))