Initial commit.

This commit is contained in:
Lucio Andrés Illanes Albornoz 2018-06-27 17:09:32 +02:00
commit 74bdd88766
10 changed files with 523 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
*.sw[op]

BIN
DejaVuSansMono.ttf Normal file

Binary file not shown.

143
ENNTool.py Executable file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python3
#
# ENNTool -- mIRC art animation tool (for EFnet #MiRCART) (WIP)
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT license.
#
# TODO:
# 1) -A: render frame #1, render frame #2, ...
# 2) -o: support at least {GIF,MP4,WEBM}
# 3) -s: effects: rotate, smash into bricks, swirl, wave, ...
# 4) XXX autodetect video width from widest mircart
# 5) XXX convert TTF to texture PNG & coords TXT, render accordingly
# 6) XXX dont stall GPU w/ glReadPixels(), switch to asynchronous model w/ PBOs
# 7) XXX render mircart as 3D blocks vs flat surface
#
from getopt import getopt, GetoptError
from glob import glob
from OpenGL.GL import *
import os, sys, time
import wx
from ENNToolGLCanvasPanel import ENNToolGLCanvasPanel
from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter
class ENNToolApp(object):
"""XXX"""
# {{{ parseArgv(self, argv): XXX
def parseArgv(self, argv):
def usage(argv0):
print("usage: {}".format(os.path.basename(argv0)), file=sys.stderr)
print(" [-A] [-f fps] [-h] [-o pname]".format(os.path.basename(argv0)), file=sys.stderr)
print(" [-p] [-r WxH] [-R WxH] [-s pname]", file=sys.stderr)
print(" [-S] [-t pname] [-v] [--] pname..", file=sys.stderr)
print("", file=sys.stderr)
print(" -a........: select animation mode", file=sys.stderr)
print(" -f fps....: set video FPS; defaults to 25", file=sys.stderr)
print(" -h........: show this screen", file=sys.stderr)
print(" -o pname..: output video pathname", file=sys.stderr)
print(" -p........: play video after rendering", file=sys.stderr)
print(" -r WxH....: set video resolution; defaults to 1152x864", file=sys.stderr)
print(" -R WxH....: set MiRCART cube resolution; defaults to 0.1x0.2", file=sys.stderr)
print(" -s pname..: input script pathname", file=sys.stderr)
print(" -S........: select scrolling mode", file=sys.stderr)
print(" -t pname..: set MiRCART texture pathname; defaults to texture.png", file=sys.stderr)
print(" -v........: be verbose", file=sys.stderr)
try:
optlist, argv = getopt(argv[1:], "Af:ho:pr:R:s:St:v")
optdict = dict(optlist)
if "-h" in optdict:
usage(sys.argv[0]); exit(0);
elif not "-o" in optdict:
raise GetoptError("-o pname must be specified")
elif not len(argv):
raise GetoptError("at least one MiRCART input pname must be specified")
if not "-f" in optdict:
optdict["-f"] = "25"
if not "-r" in optdict:
optdict["-r"] = "1152x864"
if not "-R" in optdict:
optdict["-R"] = "0.1x0.2"
if not "-t" in optdict:
optdict["-t"] = "texture.png"
if "-r" in optdict:
optdict["-r"] = [int(r) for r in optdict["-r"].split("x")][0:2]
if "-R" in optdict:
optdict["-R"] = [float(r) for r in optdict["-R"].split("x")][0:2]
except GetoptError as e:
print(e.msg); usage(sys.argv[0]); exit(1);
return argv, optdict
# }}}
# {{{ printProgress(self, progressCur, progressMax): XXX
def printProgress(self, progressCur, progressMax):
progressDiv = float(progressCur / progressMax)
if progressDiv >= 1:
progressDiv = 1; endChar = "\n";
else:
endChar = ""
print("\r[{:<50}] {}%".format(
("=" * int(progressDiv * 50)), int(progressDiv * 100)), end=endChar)
# }}}
# {{{ modeScroll(self, argv, optdict, panelGLCanvas, texturePathName, fps=25, scrollRate=0.25): XXX
def modeScroll(self, argv, optdict, panelGLCanvas, texturePathName, fps=25, scrollRate=0.25):
MiRCART = []
for inFileArg in argv:
for inFile in sorted(glob(inFileArg)):
MiRCART += ENNToolMiRCARTImporter(inFile).outMap
curY, rotateX, rotateY, translateY = 0, 0, 0, scrollRate
artTextureId = panelGLCanvas.initTexture(texturePathName)
artVbo, artVboLen, lastY, numVertices = panelGLCanvas.renderMiRCART(MiRCART, cubeSize=optdict["-R"])
if "-v" in optdict:
print("{} vertices".format(numVertices))
w, h = panelGLCanvas.GetClientSize(); w, h = max(w, 1.0), max(h, 1.0);
while True:
self.printProgress(curY, lastY)
for numFrame in range(fps):
panelGLCanvas.renderFrame(artTextureId, artVbo, artVboLen)
if translateY:
glTranslatef(0, translateY, 0); curY += translateY
if rotateX:
glRotatef(rotateX * (180.0/w), 0.0, 1.0, 0.0)
if rotateY:
glRotatef(rotateY * (180.0/h), 1.0, 0.0, 0.0)
panelGLCanvas.saveFrame()
if curY >= lastY:
self.printProgress(curY, lastY); break;
# }}}
# {{{ __init__(self, argv): XXX
def __init__(self, argv):
argv, optdict = self.parseArgv(argv)
wxApp = wx.App(False)
appFrameSize = optdict["-r"]
appFrame = wx.Frame(None, size=appFrameSize); appFrame.Hide();
appPanelSkin = wx.Panel(appFrame, wx.ID_ANY)
videoFps, videoPath = int(optdict["-f"]), optdict["-o"]
panelGLCanvas = ENNToolGLCanvasPanel(appPanelSkin, size=appFrameSize, videoPath=videoPath)
panelGLCanvas.initOpenGL(); panelGLCanvas.initVideoWriter(fps=videoFps)
if "-v" in optdict:
time0 = time.time()
self.modeScroll(argv, optdict, panelGLCanvas, fps=videoFps, texturePathName=optdict["-t"])
if "-v" in optdict:
print("delta {}s".format(time.time() - time0))
if "-p" in optdict:
os.startfile(videoPath)
# }}}
#
# Entry point
def main(*argv):
ENNToolApp(argv)
if __name__ == "__main__":
main(*sys.argv)
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

156
ENNToolGLCanvasPanel.py Normal file
View File

@ -0,0 +1,156 @@
#!/usr/bin/env python3
#
# ENNTool -- mIRC art animation tool (for EFnet #MiRCART) (WIP)
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT license.
#
# References:
# Wed, 27 Jun 2018 16:02:10 +0200 [1] <https://www.opengl.org/discussion_boards/showthread.php/125843-default-camera?p=954801&viewfull=1#post954801>
# Wed, 27 Jun 2018 16:02:11 +0200 [2] <https://www.opengl.org/discussion_boards/showthread.php/167808-2D-texture-problem-lines-between-textures>
# Wed, 27 Jun 2018 16:02:12 +0200 [3] <https://www.khronos.org/opengl/wiki/How_lighting_works#Good_Settings.>
# Wed, 27 Jun 2018 16:02:13 +0200 [4] <https://www.khronos.org/opengl/wiki/Common_Mistakes>
# Wed, 27 Jun 2018 16:02:14 +0200 [5] <https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout>
#
from OpenGL.GL import *
from PIL import Image
import cv2, numpy
import ctypes, sys
import wx, wx.glcanvas
class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
"""XXX"""
# {{{ initOpenGL(self): XXX
def initOpenGL(self):
self.glContext = wx.glcanvas.GLContext(self)
self.SetCurrent(self.glContext)
# [1]
glViewport(0, 0, *self.curSize)
glMatrixMode(GL_PROJECTION)
glLoadIdentity(); glFrustum(-1, 1, -1, 1, 1, 100);
glMatrixMode(GL_MODELVIEW)
glEnable(GL_DEPTH_TEST)
glClearColor(0, 0, 0, 1); glClearDepth(1);
glTranslatef(-5.0, 3.0, -5)
# [3]
glLight(GL_LIGHT0, GL_AMBIENT, (0, 0, 0, 1))
glLight(GL_LIGHT0, GL_DIFFUSE, (1, 1, 1, 1))
glLight(GL_LIGHT0, GL_POSITION, (1, 1, 0+2, 0))
glLight(GL_LIGHT0, GL_SPECULAR, (1, 1, 1, 1))
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (0.2, 0.2, 0.2, 1))
glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
# }}}
# {{{ initTexture(self, pathName): XXX
def initTexture(self, pathName):
artTextureId = glGenTextures(1)
artTextureImage = Image.open(pathName)
artTextureImageData = numpy.array(list(artTextureImage.getdata()), numpy.uint8)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
# [2]
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
# [4][5]
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
artTextureImage.size[0], artTextureImage.size[1],
0, GL_RGBA, GL_UNSIGNED_BYTE, artTextureImageData)
glBindTexture(GL_TEXTURE_2D, artTextureId)
return artTextureId
# }}}
# {{{ initVideoWriter(self): XXX
def initVideoWriter(self, fourcc="XVID", fps=25):
fourcc = cv2.VideoWriter_fourcc(*list(fourcc))
self.videoWriter = cv2.VideoWriter(self.videoPath, fourcc, fps, (self.width, self.height), True)
# }}}
# {{{ renderFrame(self, artTextureId, artVbo, artVboLen): XXX
def renderFrame(self, artTextureId, artVbo, artVboLen):
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glEnable(GL_TEXTURE_2D)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glBindBuffer(GL_ARRAY_BUFFER, artVbo)
glVertexPointer(3, GL_FLOAT, 32, ctypes.c_void_p(0))
glNormalPointer(GL_FLOAT, 32, ctypes.c_void_p(12))
glTexCoordPointer(2, GL_FLOAT, 32, ctypes.c_void_p(24))
glDrawArrays(GL_QUADS, 0, artVboLen)
glDisable(GL_TEXTURE_2D)
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
# }}}
# {{{ renderMiRCART(self, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2), texelHeight=0.0625, texelWidth=0.0625): XXX
def renderMiRCART(self, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2), texelHeight=0.0625, texelWidth=0.0625):
curPos = [0, 0, 0]; vertices = []; numVertices = 0;
for numRow in range(len(artMap)):
if centre and (len(artMap[numRow]) < canvasCols):
curPos[0] += (((canvasCols - len(artMap[numRow])) * cubeSize[0]) / 2)
for numCol in range(len(artMap[numRow])):
cubeColour = artMap[numRow][numCol][1] * texelWidth
# Top Right
vertices += curPos
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour+texelWidth, texelHeight]
numVertices += 1
# Top Left
vertices += [curPos[0]-cubeSize[0], curPos[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour, texelHeight]
numVertices += 1
# Bottom Left
vertices += [curPos[0]-cubeSize[0], curPos[1]-cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour, 0.0]
numVertices += 1
# Bottom Right
vertices += [curPos[0], curPos[1]-cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += [cubeColour+texelWidth, 0.0]
numVertices += 1
curPos[0] += cubeSize[0]
curPos[0], curPos[1] = 0, curPos[1] - cubeSize[1]
artVbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, artVbo)
glBufferData(GL_ARRAY_BUFFER,
(ctypes.c_float*len(vertices))(*vertices),
GL_STATIC_DRAW)
return artVbo, len(vertices), -curPos[1], numVertices
# }}}
# {{{ saveFrame(self): XXX
def saveFrame(self):
if sys.byteorder == "little":
screenshot = glReadPixels(0, 0, self.width, self.height, GL_BGR, GL_UNSIGNED_BYTE)
else:
screenshot = glReadPixels(0, 0, self.width, self.height, GL_RGB, GL_UNSIGNED_BYTE)
screenshot = numpy.flipud(numpy.frombuffer(screenshot, numpy.uint8).reshape((self.height, self.width, 3)))
self.videoWriter.write(screenshot)
# }}}
# {{{ __init__(self, parent, size, defaultPos=(24,24), videoPath=None): initialisation method
def __init__(self, parent, size, defaultPos=(24,24), videoPath=None):
super().__init__(parent, pos=defaultPos, size=size)
self.curPos = list(defaultPos); self.curSize = list(size);
self.width, self.height = self.GetClientSize()
self.videoPath = videoPath
# }}}
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

27
ENNToolMiRCARTColours.py Normal file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
#
# ENNTool -- mIRC art animation tool (for EFnet #MiRCART) (WIP)
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT license.
#
ENNToolMiRCARTColours = [
[1.00, 1.00, 1.00], # White
[0.00, 0.00, 0.00], # Black
[0.00, 0.00, 0.73], # Blue
[0.00, 0.73, 0.00], # Green
[1.00, 0.33, 0.33], # Light Red
[0.73, 0.00, 0.00], # Red
[0.73, 0.00, 0.73], # Purple
[0.73, 0.73, 0.00], # Yellow
[1.00, 1.00, 0.33], # Light Yellow
[0.33, 1.00, 0.33], # Light Green
[0.00, 0.73, 0.73], # Cyan
[0.33, 1.00, 1.00], # Light Cyan
[0.33, 0.33, 1.00], # Light Blue
[1.00, 0.33, 1.00], # Pink
[0.33, 0.33, 0.33], # Grey
[0.73, 0.73, 0.73], # Light Grey
]
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

135
ENNToolMiRCARTImporter.py Normal file
View File

@ -0,0 +1,135 @@
#!/usr/bin/env python3
#
# ENNTool -- mIRC art animation tool (for EFnet #MiRCART) (WIP)
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT license.
#
# TODO:
# 1) un-Quick'n'Dirty-ify
#
import chardet
class ENNToolMiRCARTImporter(object):
"""XXX"""
# {{{ _CellState(): Cell state
class _CellState():
CS_NONE = 0x00
CS_BOLD = 0x01
CS_ITALIC = 0x02
CS_UNDERLINE = 0x04
# }}}
# {{{ _ParseState(): Parsing loop state
class _ParseState():
PS_CHAR = 1
PS_COLOUR_DIGIT0 = 2
PS_COLOUR_DIGIT1 = 3
# }}}
# {{{ _flipCellStateBit(self, cellState, bit): XXX
def _flipCellStateBit(self, cellState, bit):
if cellState & bit:
return cellState & ~bit
else:
return cellState | bit
# }}}
# {{{ _parseCharAsColourSpec(self, colourSpec, curColours): XXX
def _parseCharAsColourSpec(self, colourSpec, curColours):
if len(colourSpec) > 0:
colourSpec = colourSpec.split(",")
if len(colourSpec) == 2 \
and len(colourSpec[1]) > 0:
return [int(colourSpec[0] or curColours[0]), \
int(colourSpec[1])]
elif len(colourSpec) == 1 \
or len(colourSpec[1]) == 0:
return [int(colourSpec[0]), curColours[1]]
else:
return [15, 1]
# }}}
# {{{ fromTextFile(self, pathName): XXX
def fromTextFile(self, pathName):
with open(pathName, "rb") as fileObject:
inFileEncoding = chardet.detect(fileObject.read())["encoding"]
self.inFile = open(pathName, "r", encoding=inFileEncoding)
self.inSize = self.outMap = None;
inCurColourSpec = ""; inCurRow = -1;
inLine = self.inFile.readline()
inSize = [0, 0]; outMap = []; inMaxCols = 0;
while inLine:
inCellState = self._CellState.CS_NONE
inParseState = self._ParseState.PS_CHAR
inCurCol = 0; inMaxCol = len(inLine);
inCurColourDigits = 0; inCurColours = [15, 1]; inCurColourSpec = "";
inCurRow += 1; outMap.append([]); inRowCols = 0; inSize[1] += 1;
while inCurCol < inMaxCol:
inChar = inLine[inCurCol]
if inChar in set("\r\n"): \
inCurCol += 1
elif inParseState == self._ParseState.PS_CHAR:
inCurCol += 1
if inChar == "":
inCellState = self._flipCellStateBit( \
inCellState, self._CellState.CS_BOLD)
elif inChar == "":
inParseState = self._ParseState.PS_COLOUR_DIGIT0
elif inChar == "":
inCellState = self._flipCellStateBit( \
inCellState, self._CellState.CS_ITALIC)
elif inChar == "":
inCellState |= self._CellState.CS_NONE
inCurColours = [15, 1]
elif inChar == "":
inCurColours = [inCurColours[1], inCurColours[0]]
elif inChar == "":
inCellState = self._flipCellStateBit( \
inCellState, self._CellState.CS_UNDERLINE)
else:
inRowCols += 1
outMap[inCurRow].append([*inCurColours, inCellState, inChar])
elif inParseState == self._ParseState.PS_COLOUR_DIGIT0 \
or inParseState == self._ParseState.PS_COLOUR_DIGIT1:
if inChar == "," \
and inParseState == self._ParseState.PS_COLOUR_DIGIT0:
if (inCurCol + 1) < inMaxCol \
and not inLine[inCurCol + 1] in set("0123456789"):
inCurColours = self._parseCharAsColourSpec( \
inCurColourSpec, inCurColours)
inCurColourDigits = 0; inCurColourSpec = "";
inParseState = self._ParseState.PS_CHAR
else:
inCurCol += 1
inCurColourDigits = 0; inCurColourSpec += inChar;
inParseState = self._ParseState.PS_COLOUR_DIGIT1
elif inChar in set("0123456789") \
and inCurColourDigits == 0:
inCurCol += 1
inCurColourDigits += 1; inCurColourSpec += inChar;
elif inChar in set("0123456789") \
and inCurColourDigits == 1 \
and inCurColourSpec[-1] == "0":
inCurCol += 1
inCurColourDigits += 1; inCurColourSpec += inChar;
elif inChar in set("012345") \
and inCurColourDigits == 1 \
and inCurColourSpec[-1] == "1":
inCurCol += 1
inCurColourDigits += 1; inCurColourSpec += inChar;
else:
inCurColours = self._parseCharAsColourSpec( \
inCurColourSpec, inCurColours)
inCurColourDigits = 0; inCurColourSpec = "";
inParseState = self._ParseState.PS_CHAR
inMaxCols = max(inMaxCols, inRowCols)
inLine = self.inFile.readline()
inSize[0] = inMaxCols; self.inSize = inSize; self.outMap = outMap;
self.inFile.close()
# }}}
# {{{ __init__(self, inFile): initialisation method
def __init__(self, inFile):
self.inFile = inFile; self.inSize = self.outMap = None;
self.fromTextFile(inFile)
# }}}
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

19
LICENCE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# ENNTool -- mIRC art animation tool (for EFnet #MiRCART) (WIP)
Copyright (c) 2018 Lucio Andrés Illanes Albornoz <<lucio@lucioillanes.de>>
This project is licensed under the terms of the MIT licence.
* Prerequisites on Windows: install Python v3.5.x and script dependencies w/ the following elevated command prompt command line:
`pip install chardet numpy opencv-python Pillow PyOpenGL wxPython`

36
puke.txt Normal file
View File

@ -0,0 +1,36 @@
0,1 3,3 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 3,3 0,1 0,0 1,1 0,1 0,0 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 1,1 9,9 1,1 9,9 1,1 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1
3,3 0,1 3,3 0,1 9,9 0,1 9,9 1,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 0,0 0,1 0,0 0,1 0,0 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1 1,1 9,9 0,1 9,9 1,1 0,1 9,9 0,1 9,9 0,1 9,9 0,1 9,9 0,1
0,1 3,3 0,1 9,9 0,1 11,11 10,10 11,11 0,1 9,9 0,1 9,9 0,1 3,3 0,1 3,3 0,1 0,0 0,1 0,0 0,1 0,0 0,1 9,9 0,1 9,9 0,1 9,9 0,1 1,1 9,9 0,1 1,1 0,1 9,9 0,1 9,9 0,1
0,1 3,3 0,1 3,3 0,1 10,10 11,11 10,10 11,11 10,10 0,1 3,3 0,1 3,3 0,1 0,0 0,1 3,3 0,1 3,3 0,1 1,1 3,3 1,1 3,3 1,1 9,9 1,1 9,9 1,1 3,3 1,1 3,3 1,1 0,1 3,3 0,1 3,3 0,1
9,9 1,1 3,3 1,1 10,10 11,11 10,10 11,11 10,10 1,1 0,0 1,1 11,11 1,10 1,1 11,11 1,1 3,3 1,1 9,9 1,1 3,3 1,1 11,11 1,1 1,10 11,11 0,1 9,9
9,9 1,1 10,10 11,11 10,10 11,11 10,10 11,11 1,1 3,3 1,1 11,11 1,10 1,1 11,11 1,1 3,3 1,1 3,3 1,1 11,11 1,1 1,10 11,11 0,1 9,9
9,9 1,1 10,10 11,11 10,10 11,11 10,10 1,1 8,8 1,1 11,11 1,10 1,1 11,11 1,1 3,3 1,1 11,11 1,1 1,10 11,11 1,1 0,1 9,9
9,9 1,1 11,11 10,10 11,11 10,10 11,11 10,10 11,11 10,10 1,1 8,8 1,1 8,8 1,1 11,11 1,10 1,1 11,11 1,1 1,10 11,11 1,1 0,1 9,9
9,9 1,1 11,11 10,10 11,11 10,10 11,10 10,10 11,11 10,10 1,1 8,8 1,1 8,8 1,1 11,11 1,10 1,1 1,10 11,11 1,1 0,1 9,9
9,9 1,1 11,11 10,10 11,11 10,10 11,11 10,10 1,1 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 11,11 1,10 11,11 1,10 11,11 1,1 0,1 9,9
1,3 9,9 1,1 11,11 10,10 11,11 10,10 11,11 1,1 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 11,11 10,10 11,11 1,1 0,1 9,9 1,3
3,3 9,9 1,1 3,3 1,1 3,3 1,1 11,11 10,10 1,1 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 3,3 9,9 3,3 1,1 0,1 9,9 1,3
3,3 9,9 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 8,8 1,1 8,8 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 3,3 9,9 1,1 0,1 9,9 1,3
3,3 9,9 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 0,0 1,1 8,8 1,1 8,8 1,1 0,0 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 3,3 9,9 3,3 1,1 0,1 9,9 1,3
3,3 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 7,7 8,8 1,1 8,8 1,1 8,8 1,1 0,0 1,1 8,8 1,1 0,0 1,1 8,8 1,1 8,8 1,1 8,8 1,1 9,9 3,3 1,1 0,1 9,9 1,3
1,3 3,3 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 7,7 8,8 1,1 8,8 1,1 8,8 1,1 3,3 9,9 3,3 1,1 0,1 9,9 1,3
11,11 3,3 9,9 1,1 7,7 8,8 1,1 8,8 1,1 8,8 1,1 8,8 1,1 3,3 9,9 1,1 0,1 9,9 1,3 11,11
11,11 3,3 9,9 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 7,7 8,8 1,1 0,0 1,1 8,8 1,1 3,3 9,9 3,3 1,1 0,1 9,9 1,3 11,11
11,11 1,3 3,3 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 7,7 8,8 1,1 8,8 1,1 9,9 3,3 1,1 0,1 9,9 1,3 11,11
10,10 3,11 1,3 3,3 9,9 1,1 3,3 1,1 3,3 1,1 7,7 8,8 1,1 0,0 1,1 0,0 1,1 0,0 1,1 8,8 1,1 3,3 1,1 9,9 1,3 3,11 10,10
10,10 3,11 1,3 3,3 9,9 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 3,3 1,1 7,7 8,8 8,1 1,1 8,1 8,8 1,1 9,9 3,3 1,1 9,9 1,3 3,11 10,10
10,10 3,11 1,3 3,3 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 9,9 1,1 7,7 8,8 8,1 0,0 1,1 0,0 1,1 0,0 8,1 8,8 1,1 3,3 9,9 1,3 3,11 10,10
10,10 3,11 3,3 9,9 7,7 8,8 8,1 1,1 8,1 8,8 7,7 9,9 3,3 1,3 3,11 10,10
10,10 11,11 3,3 7,7 8,8 8,1 0,0 8,1 8,8 7,7 3,3 11,11 10,10
10,10 11,11 3,3 9,9 7,7 8,8 8,1 8,8 7,7 9,9 3,3 11,11 10,10
10,10 11,11 3,3 9,9 3,3 7,7 3,3 9,9 3,3 11,11 10,10
10,10 11,11 3,3 9,9 3,3 11,11 3,3 9,9 3,3 11,11 10,10
11,11 3,3 9,9 3,3 11,11 10,10 11,11 3,3 9,9 3,3 11,11
11,11 3,3 9,9 3,3 11,11 10,10 11,11 10,10 11,11 3,3 9,9 3,3 11,11
11,11 3,3 9,9 3,3 11,11 10,10 11,11 10,10 11,11 10,10 11,11 3,3 9,9 3,3 11,11
11,11 3,3 9,9 3,3 11,11 10,10 11,11 10,10 11,11 3,3 9,9 3,3 11,11
10,10 11,11 3,3 9,9 3,3 11,11 10,10 11,11 3,3 9,9 3,3 11,11 10,10
10,10 11,11 3,3 9,9 3,3 11,11 3,3 9,9 3,3 11,11 10,10
10,10 11,11 3,3 9,9 3,3 9,9 3,3 11,11 10,10
10,10 11,11 3,3 9,9 3,3 11,11 10,10
10,10 11,11 3,3 11,11 10,10

BIN
texture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B