Initial commit of the TextureCreator
This commit is contained in:
parent
a5e2744fc0
commit
707d234e2b
39 changed files with 1547 additions and 0 deletions
326
TextureCreator/lib/TexturePainter.py
Normal file
326
TextureCreator/lib/TexturePainter.py
Normal file
|
@ -0,0 +1,326 @@
|
|||
'''
|
||||
This script is part of the FURWARE texture creator, a texture
|
||||
generation script for the FURWARE text text-on-prim script
|
||||
for Second Life(R).
|
||||
|
||||
Please see the included README and LICENSE files in the root
|
||||
directory for more information.
|
||||
'''
|
||||
|
||||
'''
|
||||
This script is the heart of the texture creator. It creates a
|
||||
texture, executes script files to perform actions on it and
|
||||
writes the result out to an image file.
|
||||
'''
|
||||
|
||||
import cairo
|
||||
import itertools
|
||||
import math
|
||||
import os
|
||||
|
||||
from . import FontFaceCreator
|
||||
from . import TGAWriter
|
||||
from ScriptReader import ScriptReader
|
||||
|
||||
OUTPUT_DIR = "output"
|
||||
SCRIPTS_DIR = "scripts"
|
||||
|
||||
class GridConfig:
|
||||
def __init__(self, args):
|
||||
self.imageSize = (args[0], args[1])
|
||||
self.cellSize = (32, 64)
|
||||
self.begin = (32, 44)
|
||||
self.spacing = (40, 72)
|
||||
self.cellCount = (25, 14)
|
||||
|
||||
class CharConfig:
|
||||
def __init__(self, args):
|
||||
self.offsetX = args[0]
|
||||
self.offsetY = args[1]
|
||||
self.scaleX = args[2]
|
||||
self.scaleY = args[3]
|
||||
|
||||
class FontConfig:
|
||||
def __init__(self, fontConfPath):
|
||||
self.offsetX = 0
|
||||
self.offsetY = 0
|
||||
self.offsetXUnit = ""
|
||||
self.offsetYUnit = ""
|
||||
self.perCharConfigs = {}
|
||||
self.guessingChar = "X"
|
||||
self.scale = 0.75
|
||||
self.scaleUnit = "cellHeight"
|
||||
self.autoShrink = False
|
||||
|
||||
if not os.path.exists(fontConfPath):
|
||||
return
|
||||
|
||||
fontConf = ScriptReader(fontConfPath)
|
||||
|
||||
while fontConf.more():
|
||||
if fontConf.getCmd() == "offset":
|
||||
[self.offsetX, self.offsetY, self.offsetXUnit, self.offsetYUnit] = fontConf.getArgs([int, int, str, str])
|
||||
|
||||
elif fontConf.getCmd() == "scale":
|
||||
[self.scale, self.scaleUnit] = fontConf.getArgs([float, str])
|
||||
|
||||
elif fontConf.getCmd() == "guessingChar":
|
||||
self.guessingChar = fontConf.getArgs([unicode])[0]
|
||||
|
||||
elif fontConf.getCmd() == "enableAutoShrink":
|
||||
self.autoShrink = True
|
||||
|
||||
elif fontConf.getCmd() == "charOffset":
|
||||
args = fontConf.getArgs([unicode, int, int, float, float])
|
||||
self.perCharConfigs[args[0]] = CharConfig(args[1:])
|
||||
|
||||
def getCharConfig(self, char):
|
||||
try:
|
||||
return self.perCharConfigs[char]
|
||||
except KeyError:
|
||||
return CharConfig([0, 0, 1.0, 1.0])
|
||||
|
||||
class FontExtents:
|
||||
def __init__(self, cairoContext):
|
||||
self.cairoContext = cairoContext
|
||||
|
||||
def update(self, text):
|
||||
self.xBearing, self.yBearing, self.xSize, self.ySize, self.xAdvance, self.yAdvance \
|
||||
= self.cairoContext.text_extents(text)
|
||||
|
||||
class TexturePainter:
|
||||
def __init__(self, outputName):
|
||||
self.debugGrid = False
|
||||
self.outputName = outputName
|
||||
self.defaultOutputName = outputName
|
||||
|
||||
def runScript(self, scriptName, defaultFont):
|
||||
scriptPath = os.path.join(os.path.dirname(defaultFont), scriptName)
|
||||
if not os.path.exists(scriptPath):
|
||||
# Then try the default scripts directory.
|
||||
scriptPath = os.path.join(SCRIPTS_DIR, scriptName)
|
||||
if not os.path.exists(scriptPath):
|
||||
print(" WARNING: Script \"" + scriptName + "\" not found.")
|
||||
return False
|
||||
|
||||
script = ScriptReader(scriptPath)
|
||||
|
||||
while script.more():
|
||||
if script.getCmd() == "runScript":
|
||||
if not self.runScript(script.getRawArg(), defaultFont):
|
||||
return False
|
||||
|
||||
elif script.getCmd() == "noBuild":
|
||||
return False
|
||||
|
||||
elif script.getCmd() == "setOutputName":
|
||||
self.outputName = script.getRawArg()
|
||||
|
||||
elif script.getCmd() == "setOutputNameSuffix":
|
||||
self.outputName = self.defaultOutputName + script.getRawArg()
|
||||
|
||||
elif script.getCmd() == "init":
|
||||
self.init(script.getArgs([int, int, float, float]))
|
||||
|
||||
elif script.getCmd() == "setCellSize":
|
||||
self.gridConfig.cellSize = script.getArgs([int, int])
|
||||
|
||||
elif script.getCmd() == "setCellOffset":
|
||||
self.gridConfig.begin = script.getArgs([int, int])
|
||||
|
||||
elif script.getCmd() == "setCellSpacing":
|
||||
self.gridConfig.spacing = script.getArgs([int, int])
|
||||
|
||||
elif script.getCmd() == "setCellCount":
|
||||
self.gridConfig.cellCount = script.getArgs([int, int])
|
||||
|
||||
elif script.getCmd() == "drawLineBetweenCells":
|
||||
self.lineBetweenCells(*[int(arg) for arg in script.getArgs([int, int, int, int, int, int, int, int, int])])
|
||||
|
||||
elif script.getCmd() == "drawRectBetweenCells":
|
||||
self.rectangleBetweenCells(*[int(arg) for arg in script.getArgs([int, int, int, int, int, int, int, int, int])])
|
||||
|
||||
elif script.getCmd() == "drawFilledRectBetweenCells":
|
||||
self.rectangleBetweenCells(*[int(arg) for arg in script.getArgs([int, int, int, int, int, int, int, int, int])], filled = True)
|
||||
|
||||
elif script.getCmd() == "drawChars":
|
||||
for char in script.getRawArg():
|
||||
self.drawChar(char)
|
||||
|
||||
elif script.getCmd() == "loadFont":
|
||||
fontPath = script.getRawArg()
|
||||
if fontPath == "":
|
||||
fontPath = defaultFont
|
||||
self.loadFont(fontPath)
|
||||
|
||||
elif script.getCmd() == "jumpToCell":
|
||||
self.jumpToCell(*script.getArgs([int, int]))
|
||||
|
||||
elif script.getCmd() == "drawDebugGrid":
|
||||
self.debugGrid = True
|
||||
|
||||
self.cairoContext.save()
|
||||
self.cairoContext.set_source_rgb(0,0,0)
|
||||
self.cairoContext.set_matrix(cairo.Matrix(
|
||||
xx = self.gridConfig.imageSize[0],
|
||||
yy = self.gridConfig.imageSize[1]
|
||||
))
|
||||
self.cairoContext.rectangle(0,0,1,1)
|
||||
self.cairoContext.fill()
|
||||
self.cairoContext.restore()
|
||||
|
||||
self.cairoContext.save()
|
||||
self.cairoContext.set_source_rgb(0.5,0.5,0.5)
|
||||
thickness = 2
|
||||
for y in range(0, self.gridConfig.cellCount[1]):
|
||||
for x in range(0, self.gridConfig.cellCount[0]):
|
||||
dx = self.gridConfig.cellSize[0]/2 + thickness/2
|
||||
dy = self.gridConfig.cellSize[1]/2 + thickness/2
|
||||
self.rectangleBetweenCells(thickness, x, y, x, y, -dx, -dy, dx, dy)
|
||||
self.cairoContext.restore()
|
||||
|
||||
else:
|
||||
print(" ERROR: Unknown script command \"" + script.getCmd() + "\".")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def writeTexture(self):
|
||||
self.cairoSurface.flush()
|
||||
buf = self.cairoSurface.get_data()
|
||||
|
||||
arr = bytearray(buf)
|
||||
|
||||
del arr[0::4]
|
||||
del arr[0::3]
|
||||
|
||||
if not self.debugGrid:
|
||||
arr[0::2] = itertools.repeat(255, self.gridConfig.imageSize[0] * self.gridConfig.imageSize[1])
|
||||
|
||||
targetPath = os.path.join(OUTPUT_DIR, self.outputName + ".tga")
|
||||
|
||||
TGAWriter.writeTGA(self.gridConfig.imageSize[0], self.gridConfig.imageSize[1], 8, 16, arr, targetPath)
|
||||
|
||||
def init(self, args):
|
||||
imageSizeX = int(math.floor(args[2] * args[0]))
|
||||
imageSizeY = int(math.floor(args[3] * args[1]))
|
||||
|
||||
self.gridConfig = GridConfig([imageSizeX, imageSizeY])
|
||||
|
||||
self.cairoSurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, imageSizeX, imageSizeY)
|
||||
|
||||
self.cairoContext = cairo.Context(self.cairoSurface)
|
||||
self.cairoContext.set_matrix(cairo.Matrix(
|
||||
xx = args[2], yy = args[3]
|
||||
))
|
||||
|
||||
self.cairoContext.set_source_rgb(1,1,1)
|
||||
|
||||
fontOptions = cairo.FontOptions()
|
||||
fontOptions.set_antialias(cairo.ANTIALIAS_GRAY)
|
||||
fontOptions.set_hint_style(cairo.HINT_STYLE_NONE)
|
||||
|
||||
self.cairoContext.set_font_options(fontOptions)
|
||||
|
||||
self.currentRow = 0
|
||||
self.currentCol = 0
|
||||
|
||||
def loadFont(self, fontPath):
|
||||
self.fontConfig = FontConfig(os.path.join(os.path.dirname(fontPath), "font.conf"))
|
||||
|
||||
fontFace = FontFaceCreator.fontFaceFromFile(fontPath)
|
||||
|
||||
self.cairoContext.set_font_face(fontFace)
|
||||
self.fontSize = self.fontConfig.scale * self.convertScaleUnit(self.fontConfig.scaleUnit)
|
||||
self.cairoContext.set_font_size(self.fontSize)
|
||||
|
||||
self.fontExtents = FontExtents(self.cairoContext)
|
||||
self.fontExtents.update(self.fontConfig.guessingChar)
|
||||
|
||||
self.guessedYOffset = self.fontExtents.ySize / 2
|
||||
|
||||
def jumpToCell(self, x, y):
|
||||
self.currentCol = int(x)
|
||||
self.currentRow = int(y)
|
||||
|
||||
def drawChar(self, char):
|
||||
fontSizeX = self.fontSize
|
||||
fontSizeY = self.fontSize
|
||||
|
||||
if self.fontConfig.autoShrink:
|
||||
self.cairoContext.set_font_size(1)
|
||||
self.fontExtents.update(char)
|
||||
|
||||
if self.fontExtents.xSize > 0:
|
||||
widthGuess = self.gridConfig.cellSize[0] / self.fontExtents.xSize
|
||||
if widthGuess < self.fontSize:
|
||||
fontSizeX = widthGuess
|
||||
|
||||
charConfig = self.fontConfig.getCharConfig(char)
|
||||
|
||||
fontSizeX *= charConfig.scaleX
|
||||
fontSizeY *= charConfig.scaleY
|
||||
|
||||
self.cairoContext.set_font_matrix(cairo.Matrix(xx = fontSizeX, yy = fontSizeY))
|
||||
self.fontExtents.update(char)
|
||||
|
||||
xOffset = -self.fontExtents.xAdvance / 2
|
||||
yOffset = self.guessedYOffset
|
||||
|
||||
xOffset += self.fontConfig.offsetX * self.convertScaleUnit(self.fontConfig.offsetXUnit)
|
||||
yOffset += self.fontConfig.offsetY * self.convertScaleUnit(self.fontConfig.offsetYUnit)
|
||||
|
||||
xOffset += charConfig.offsetX
|
||||
yOffset += charConfig.offsetY
|
||||
|
||||
x = self.gridConfig.begin[0] + self.currentCol * self.gridConfig.spacing[0] + xOffset
|
||||
y = self.gridConfig.begin[1] + self.currentRow * self.gridConfig.spacing[1] + yOffset
|
||||
|
||||
self.cairoContext.move_to(x, y)
|
||||
self.cairoContext.show_text(char)
|
||||
|
||||
self.currentCol += 1
|
||||
if self.currentCol >= self.gridConfig.cellCount[0]:
|
||||
self.currentRow += 1
|
||||
self.currentCol = 0
|
||||
|
||||
def cellPos(self, x, y):
|
||||
return (
|
||||
self.gridConfig.begin[0] + x * self.gridConfig.spacing[0],
|
||||
self.gridConfig.begin[1] + y * self.gridConfig.spacing[1]
|
||||
)
|
||||
|
||||
def lineBetweenCells(self, lineWidth, x0, y0, x1, y1, dx0 = 0, dy0 = 0, dx1 = 0, dy1 = 0):
|
||||
self.cairoContext.set_line_width(lineWidth)
|
||||
beg = self.cellPos(x0, y0)
|
||||
end = self.cellPos(x1, y1)
|
||||
begD = (dx0, dy0)
|
||||
endD = (dx1, dy1)
|
||||
self.cairoContext.move_to(beg[0]+begD[0], beg[1]+begD[1])
|
||||
self.cairoContext.line_to(end[0]+endD[0], end[1]+endD[1])
|
||||
self.cairoContext.stroke()
|
||||
|
||||
def rectangleBetweenCells(self, lineWidth, begX, begY, endX, endY, \
|
||||
begDX = 0, begDY = 0, endDX = 0, endDY = 0, filled = False):
|
||||
self.cairoContext.set_line_width(lineWidth)
|
||||
beg = self.cellPos(begX, begY)
|
||||
end = self.cellPos(endX, endY)
|
||||
begD = (begDX, begDY)
|
||||
endD = (endDX, endDY)
|
||||
self.cairoContext.rectangle(
|
||||
beg[0]+begD[0],
|
||||
beg[1]+begD[1],
|
||||
end[0]-beg[0]-begD[0]+endD[0],
|
||||
end[1]-beg[1]-begD[1]+endD[1]
|
||||
)
|
||||
if filled:
|
||||
self.cairoContext.fill()
|
||||
else:
|
||||
self.cairoContext.stroke()
|
||||
|
||||
def convertScaleUnit(self, unitString):
|
||||
if unitString == "cellWidth":
|
||||
return self.gridConfig.cellSize[0]
|
||||
elif unitString == "cellHeight":
|
||||
return self.gridConfig.cellSize[1]
|
||||
return 1
|
Loading…
Add table
Add a link
Reference in a new issue