Initial FFmpeg & {GIF,MP4,WEBM,...} support for -o.

ENNToolGLCanvasPanel.py: minor cleanup regarding deprecated calls.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2018-07-03 18:17:23 +02:00
parent e656f549ff
commit eff72be6ef
6 changed files with 104 additions and 121 deletions

View File

@ -6,14 +6,12 @@
# #
# TODO: # TODO:
# 1) -A: render frame #1, render frame #2, ... # 1) -A: render frame #1, render frame #2, ...
# 2) -o: support at least {GIF,MP4,WEBM} # 2) -s: effects: rotate, smash into bricks, swirl, wave, ...
# 3) -s: effects: rotate, smash into bricks, swirl, wave, ... # 3) Feature: include ETA @ progress bar
# 4) Feature: include ETA @ progress bar # 4) Feature: autodetect video width from widest mircart
# 5) Feature: autodetect video width from widest mircart # 5) Feature: render mircart as 3D blocks vs flat surface
# 6) Feature: render mircart as 3D blocks vs flat surface # 6) Optimisation: dont stall GPU w/ glReadPixels(), switch to asynchronous model w/ FBO or PBO (http://www.songho.ca/opengl/gl_fbo.html, http://www.songho.ca/opengl/gl_pbo.html)
# 7) Optimisation: dont stall GPU w/ glReadPixels(), switch to asynchronous model w/ FBO or PBO (http://www.songho.ca/opengl/gl_fbo.html, http://www.songho.ca/opengl/gl_pbo.html) # 7) OpenGL: use VAOs + glVertexAttribFormat + glVertexAttribBinding
# 8) OpenGL: use VAOs + glVertexAttribFormat + glVertexAttribBinding
# 9) OpenGL: use glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) & multiply texel * bgcolour (https://learnopengl.com/Advanced-OpenGL/Blending)
# #
from getopt import getopt, GetoptError from getopt import getopt, GetoptError
@ -24,6 +22,7 @@ import wx
from ENNToolGLCanvasPanel import ENNToolGLCanvasPanel from ENNToolGLCanvasPanel import ENNToolGLCanvasPanel
from ENNToolGLTTFTexture import ENNToolGLTTFTexture from ENNToolGLTTFTexture import ENNToolGLTTFTexture
from ENNToolGLVideoWriter import ENNToolGLVideoWriter
from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter
class ENNToolApp(object): class ENNToolApp(object):
@ -33,31 +32,30 @@ class ENNToolApp(object):
def parseArgv(self, argv): def parseArgv(self, argv):
def usage(argv0): def usage(argv0):
print("usage: {}".format(os.path.basename(argv0)), file=sys.stderr) 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(" [-A] [-f fps] [-h] [-o fname]".format(os.path.basename(argv0)), file=sys.stderr)
print(" [-p] [-r WxH] [-R WxH] [-s pname]", file=sys.stderr) print(" [-p] [-r WxH] [-R WxH] [-s fname]", file=sys.stderr)
print(" [-S] [-t pname] [-v] [--] pname..", file=sys.stderr) print(" [-S] [-v] [--] fname..", file=sys.stderr)
print("", file=sys.stderr) print("", file=sys.stderr)
print(" -a........: select animation mode", file=sys.stderr) print(" -a........: select animation mode", file=sys.stderr)
print(" -f fps....: set video FPS; defaults to 25", file=sys.stderr) print(" -f fps....: set video FPS; defaults to 25", file=sys.stderr)
print(" -h........: show this screen", file=sys.stderr) print(" -h........: show this screen", file=sys.stderr)
print(" -o pname..: output video pathname", file=sys.stderr) print(" -o fname..: output video filename; extension determines video type", file=sys.stderr)
print(" -p........: play video after rendering", 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 video resolution; defaults to 1152x864", file=sys.stderr)
print(" -R WxH....: set MiRCART cube resolution; defaults to 0.1x0.2", 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 fname..: input script filename", file=sys.stderr)
print(" -S........: select scrolling mode", file=sys.stderr) print(" -S........: select scrolling mode", file=sys.stderr)
print(" -t pname..: set MiRCART texture pathname; defaults to {}".format(os.path.join("assets", "texture.png")), file=sys.stderr)
print(" -v........: be verbose", file=sys.stderr) print(" -v........: be verbose", file=sys.stderr)
try: try:
optlist, argv = getopt(argv[1:], "Af:ho:pr:R:s:St:v") optlist, argv = getopt(argv[1:], "Af:ho:pr:R:s:Sv")
optdict = dict(optlist) optdict = dict(optlist)
if "-h" in optdict: if "-h" in optdict:
usage(sys.argv[0]); exit(0); usage(sys.argv[0]); exit(0);
elif not "-o" in optdict: elif not "-o" in optdict:
raise GetoptError("-o pname must be specified") raise GetoptError("-o fname must be specified")
elif not len(argv): elif not len(argv):
raise GetoptError("at least one MiRCART input pname must be specified") raise GetoptError("at least one MiRCART input fname must be specified")
if not "-f" in optdict: if not "-f" in optdict:
optdict["-f"] = "25" optdict["-f"] = "25"
@ -65,8 +63,6 @@ class ENNToolApp(object):
optdict["-r"] = "1152x864" optdict["-r"] = "1152x864"
if not "-R" in optdict: if not "-R" in optdict:
optdict["-R"] = "0.1x0.2" optdict["-R"] = "0.1x0.2"
if not "-t" in optdict:
optdict["-t"] = os.path.join("assets", "texture.png")
if "-r" in optdict: if "-r" in optdict:
optdict["-r"] = [int(r) for r in optdict["-r"].split("x")][0:2] optdict["-r"] = [int(r) for r in optdict["-r"].split("x")][0:2]
@ -86,8 +82,8 @@ class ENNToolApp(object):
print("\r[{:<50}] {}%".format( print("\r[{:<50}] {}%".format(
("=" * int(progressDiv * 50)), int(progressDiv * 100)), end=endChar) ("=" * int(progressDiv * 50)), int(progressDiv * 100)), end=endChar)
# }}} # }}}
# {{{ modeScroll(self, argv, optdict, panelGLCanvas, texturePathName, fps=25, scrollRate=0.25): XXX # {{{ modeScroll(self, argv, optdict, GLVideoWriter, panelGLCanvas, fps=25, scrollRate=0.25): XXX
def modeScroll(self, argv, optdict, panelGLCanvas, texturePathName, fps=25, scrollRate=0.25): def modeScroll(self, argv, optdict, GLVideoWriter, panelGLCanvas, fps=25, scrollRate=0.25):
MiRCART = [] MiRCART = []
for inFileArg in argv: for inFileArg in argv:
for inFile in sorted(glob(inFileArg)): for inFile in sorted(glob(inFileArg)):
@ -110,9 +106,11 @@ class ENNToolApp(object):
glRotatef(rotateX * (180.0/w), 0.0, 1.0, 0.0) glRotatef(rotateX * (180.0/w), 0.0, 1.0, 0.0)
if rotateY: if rotateY:
glRotatef(rotateY * (180.0/h), 1.0, 0.0, 0.0) glRotatef(rotateY * (180.0/h), 1.0, 0.0, 0.0)
panelGLCanvas.saveFrame() GLVideoWriter.saveFrame()
if curY >= lastY: if curY >= lastY:
self.printProgress(curY, lastY); break; self.printProgress(curY, lastY); break;
GLVideoWriter.saveVideo()
# }}} # }}}
# {{{ __init__(self, argv): XXX # {{{ __init__(self, argv): XXX
def __init__(self, argv): def __init__(self, argv):
@ -123,14 +121,14 @@ class ENNToolApp(object):
appPanelSkin = wx.Panel(appFrame, wx.ID_ANY) appPanelSkin = wx.Panel(appFrame, wx.ID_ANY)
videoFps, videoPath = int(optdict["-f"]), optdict["-o"] videoFps, videoPath = int(optdict["-f"]), optdict["-o"]
panelGLCanvas = ENNToolGLCanvasPanel(appPanelSkin, size=appFrameSize, videoPath=videoPath) panelGLCanvas = ENNToolGLCanvasPanel(appPanelSkin, size=appFrameSize)
panelGLCanvas.initOpenGL() panelGLCanvas.initOpenGL()
panelGLCanvas.initShaders() panelGLCanvas.initShaders()
panelGLCanvas.initVideoWriter(fps=videoFps) GLVideoWriter = ENNToolGLVideoWriter(videoPath, panelGLCanvas.GetClientSize(), videoFps=videoFps)
if "-v" in optdict: if "-v" in optdict:
time0 = time.time() time0 = time.time()
self.modeScroll(argv, optdict, panelGLCanvas, fps=videoFps, texturePathName=optdict["-t"]) self.modeScroll(argv, optdict, GLVideoWriter, panelGLCanvas, fps=videoFps)
if "-v" in optdict: if "-v" in optdict:
print("delta {}s".format(time.time() - time0)) print("delta {}s".format(time.time() - time0))
if "-p" in optdict: if "-p" in optdict:

View File

@ -11,15 +11,12 @@
# Wed, 27 Jun 2018 16:02:13 +0200 [4] <https://www.khronos.org/opengl/wiki/Common_Mistakes> # 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> # Wed, 27 Jun 2018 16:02:14 +0200 [5] <https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout>
# Thu, 28 Jun 2018 18:32:50 +0200 [6] <https://stackoverflow.com/questions/18935203/shader-position-vec4-or-vec3> # Thu, 28 Jun 2018 18:32:50 +0200 [6] <https://stackoverflow.com/questions/18935203/shader-position-vec4-or-vec3>
# Tue, 03 Jul 2018 14:34:57 +0200 [7] <https://gamedev.stackexchange.com/questions/107793/binding-and-unbinding-what-would-you-do>
# #
from OpenGL.GL import * from OpenGL.GL import *
from OpenGL.GL import shaders from OpenGL.GL import shaders
import cv2, numpy import ctypes, wx, wx.glcanvas
import ctypes, os, sys, time
import wx, wx.glcanvas
from ENNToolMiRCARTColours import ENNToolMiRCARTColoursFloat
class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel): class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
"""XXX""" """XXX"""
@ -40,10 +37,10 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
# }}} # }}}
# {{{ initShaders(self): XXX # {{{ initShaders(self): XXX
def initShaders(self): def initShaders(self):
# Fragment shader
fs = shaders.compileShader(""" fs = shaders.compileShader("""
#version 330 core #version 330 core
in vec4 bgColour;
in vec2 fgTexCoord; in vec2 fgTexCoord;
uniform sampler2D texture; uniform sampler2D texture;
@ -52,125 +49,83 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
gl_FragColor = vec4(texel.r, texel.g, texel.b, 1.0); gl_FragColor = vec4(texel.r, texel.g, texel.b, 1.0);
} }
""", GL_FRAGMENT_SHADER) """, GL_FRAGMENT_SHADER)
# Vertex shader
vs = shaders.compileShader(""" vs = shaders.compileShader("""
#version 330 core #version 330 core
layout(location = 0) in vec4 vertex; layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal; layout(location = 1) in vec2 texcoord;
layout(location = 2) in vec4 colour;
layout(location = 3) in vec2 texcoord;
out vec4 bgColour;
out vec2 fgTexCoord; out vec2 fgTexCoord;
uniform mat4 model; uniform mat4 modelview;
uniform mat4 projection; uniform mat4 projection;
void main() { void main() {
gl_Position = projection * model * vertex; gl_Position = projection * modelview * vertex;
bgColour = colour;
fgTexCoord = texcoord; fgTexCoord = texcoord;
} }
""", GL_VERTEX_SHADER) """, GL_VERTEX_SHADER)
self.shader = shaders.compileProgram(vs, fs) self.shader = shaders.compileProgram(vs, fs)
# }}} # }}}
# {{{ 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 # {{{ renderFrame(self, artTextureId, artVbo, artVboLen): XXX
def renderFrame(self, artTextureId, artVbo, artVboLen): def renderFrame(self, artTextureId, artVbo, artVboLen):
glEnableClientState(GL_VERTEX_ARRAY) # Bind VBO and named texture & install shader program object
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glBindBuffer(GL_ARRAY_BUFFER, artVbo) glBindBuffer(GL_ARRAY_BUFFER, artVbo)
glBindTexture(GL_TEXTURE_2D, artTextureId)
glUseProgram(self.shader) glUseProgram(self.shader)
model = (GLfloat * 16)()
glGetFloatv(GL_MODELVIEW_MATRIX, model) # Specify modelview and projection matrix & texture unit uniforms for shader programs
projection = (GLfloat * 16)() modelview, projection = (GLfloat * 16)(), (GLfloat * 16)()
glGetFloatv(GL_MODELVIEW_MATRIX, modelview)
glGetFloatv(GL_PROJECTION_MATRIX, projection) glGetFloatv(GL_PROJECTION_MATRIX, projection)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "model"), 1, GL_FALSE, model) glUniformMatrix4fv(glGetUniformLocation(self.shader, "modelview"), 1, GL_FALSE, modelview)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "projection"), 1, GL_FALSE, projection) glUniformMatrix4fv(glGetUniformLocation(self.shader, "projection"), 1, GL_FALSE, projection)
glUniform1i(glGetUniformLocation(self.shader, "texture"), 0) glUniform1i(glGetUniformLocation(self.shader, "texture"), 0)
# [6] # VBO vertices location
glEnableVertexAttribArray(0) glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, False, 48, ctypes.c_void_p(0)) glVertexAttribPointer(0, 3, GL_FLOAT, False, 20, ctypes.c_void_p(0))
glVertexPointer(3, GL_FLOAT, 20, ctypes.c_void_p(0))
# VBO texture coordinates
glEnableVertexAttribArray(1) glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 3, GL_FLOAT, False, 48, ctypes.c_void_p(12)) glVertexAttribPointer(1, 2, GL_FLOAT, False, 20, ctypes.c_void_p(12))
glEnableVertexAttribArray(2) glTexCoordPointer(2, GL_FLOAT, 20, ctypes.c_void_p(12))
glVertexAttribPointer(2, 4, GL_FLOAT, False, 48, ctypes.c_void_p(24))
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 2, GL_FLOAT, False, 48, ctypes.c_void_p(40))
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) # Clear colour and depth buffer, draw quads from VBO & clear state
glVertexPointer(3, GL_FLOAT, 48, ctypes.c_void_p(0)) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glNormalPointer(GL_FLOAT, 48, ctypes.c_void_p(12))
glColorPointer(4, GL_FLOAT, 48, ctypes.c_void_p(24))
glTexCoordPointer(2, GL_FLOAT, 48, ctypes.c_void_p(40))
glDrawArrays(GL_QUADS, 0, artVboLen) glDrawArrays(GL_QUADS, 0, artVboLen)
glDisableVertexAttribArray(0) glDisableVertexAttribArray(0)
glDisable(GL_BLEND) glBindTexture(GL_TEXTURE_2D, 0)
glDisable(GL_TEXTURE_2D)
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
# }}} # }}}
# {{{ renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)): XXX # {{{ renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)): XXX
def renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)): def renderMiRCART(self, artInfo, artMap, centre=True, canvasCols=100, cubeSize=(0.1, 0.2)):
curPos = [0, 0, 0]; vertices = []; numVertices = 0; curPos, vertices, numVertices = [0, 0, 0], [], 0
for numRow in range(len(artMap)): for numRow in range(len(artMap)):
if centre and (len(artMap[numRow]) < canvasCols): if centre and (len(artMap[numRow]) < canvasCols):
curPos[0] += (((canvasCols - len(artMap[numRow])) * cubeSize[0]) / 2) curPos[0] += (((canvasCols - len(artMap[numRow])) * cubeSize[0]) / 2)
for numCol in range(len(artMap[numRow])): for numCol in range(len(artMap[numRow])):
cubeFg = artMap[numRow][numCol][0] cubeFg = artMap[numRow][numCol][0]
cubeBg = artMap[numRow][numCol][1] cubeBg = artMap[numRow][numCol][1]
cubeBgFloat = [*ENNToolMiRCARTColoursFloat[cubeBg], 1.0]
cubeAttrs = artMap[numRow][numCol][2] cubeAttrs = artMap[numRow][numCol][2]
cubeChar = artMap[numRow][numCol][3] cubeChar = artMap[numRow][numCol][3]
artCell = artInfo[cubeFg][cubeBg][cubeAttrs][cubeChar] artCell = artInfo[cubeFg][cubeBg][cubeAttrs][cubeChar]
# Top Right # Top Right, Top Left
vertices += curPos vertices += curPos
vertices += [0.0, 0.0, 1.0]
vertices += cubeBgFloat
vertices += artCell[0:2] vertices += artCell[0:2]
numVertices += 1 vertices += [curPos[0] - cubeSize[0], curPos[1], curPos[2]]
# Top Left
vertices += [curPos[0]-cubeSize[0], curPos[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += cubeBgFloat
vertices += artCell[2:4] vertices += artCell[2:4]
numVertices += 1
# Bottom Left # Bottom Left, Bottom Right
vertices += [curPos[0]-cubeSize[0], curPos[1]-cubeSize[1], curPos[2]] vertices += [curPos[0] - cubeSize[0], curPos[1] - cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += cubeBgFloat
vertices += artCell[4:6] vertices += artCell[4:6]
numVertices += 1 vertices += [curPos[0], curPos[1] - cubeSize[1], curPos[2]]
# Bottom Right
vertices += [curPos[0], curPos[1]-cubeSize[1], curPos[2]]
vertices += [0.0, 0.0, 1.0]
vertices += cubeBgFloat
vertices += artCell[6:8] vertices += artCell[6:8]
numVertices += 1
curPos[0] += cubeSize[0] curPos[0], numVertices = curPos[0] + cubeSize[0], numVertices + 4
curPos[0], curPos[1] = 0, curPos[1] - cubeSize[1] curPos[0], curPos[1] = 0, curPos[1] - cubeSize[1]
artVbo = glGenBuffers(1) artVbo = glGenBuffers(1)
@ -180,22 +135,10 @@ class ENNToolGLCanvasPanel(wx.glcanvas.GLCanvas, wx.Panel):
GL_STATIC_DRAW) GL_STATIC_DRAW)
return artVbo, len(vertices), -curPos[1], numVertices return artVbo, len(vertices), -curPos[1], numVertices
# }}} # }}}
# {{{ saveFrame(self): XXX # {{{ __init__(self, parent, size, defaultPos=(24,24)): initialisation method
def saveFrame(self): def __init__(self, parent, size, defaultPos=(24,24)):
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) super().__init__(parent, pos=defaultPos, size=size)
self.curPos = list(defaultPos); self.curSize = list(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 # vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -17,7 +17,6 @@ from OpenGL.GL import *
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
import numpy import numpy
import os, string, sys import os, string, sys
from ENNToolMiRCARTColours import ENNToolMiRCARTColours from ENNToolMiRCARTColours import ENNToolMiRCARTColours
from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter from ENNToolMiRCARTImporter import ENNToolMiRCARTImporter
@ -34,7 +33,6 @@ class ENNToolGLTTFTexture(object):
def _nestedDict(): def _nestedDict():
return defaultdict(ENNToolGLTTFTexture._nestedDict) return defaultdict(ENNToolGLTTFTexture._nestedDict)
# }}} # }}}
# {{{ _drawCharList(self, artInfo, charList, pilFontBold, pilFontNormal, pilFontSize, pilImageDraw, pilImageSize): XXX # {{{ _drawCharList(self, artInfo, charList, pilFontBold, pilFontNormal, pilFontSize, pilImageDraw, pilImageSize): XXX
def _drawCharList(self, artInfo, charList, pilFontBold, pilFontNormal, pilFontSize, pilImageDraw, pilImageSize): def _drawCharList(self, artInfo, charList, pilFontBold, pilFontNormal, pilFontSize, pilImageDraw, pilImageSize):
curPos = [0, 0] curPos = [0, 0]
@ -119,9 +117,9 @@ class ENNToolGLTTFTexture(object):
0, GL_RGBA, GL_UNSIGNED_BYTE, artTextureImageData) 0, GL_RGBA, GL_UNSIGNED_BYTE, artTextureImageData)
glGenerateMipmap(GL_TEXTURE_2D) glGenerateMipmap(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
return artTextureId return artTextureId
# }}} # }}}
# {{{ getParams(self): XXX # {{{ getParams(self): XXX
def getParams(self): def getParams(self):
return self.artTextureId, self.artInfo return self.artTextureId, self.artInfo

44
ENNToolGLVideoWriter.py Normal file
View File

@ -0,0 +1,44 @@
#!/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.
#
from OpenGL.GL import *
import numpy, subprocess
class ENNToolGLVideoWriter(object):
"""XXX"""
# {{{ saveFrame(self): XXX
def saveFrame(self):
frameBuffer = glReadPixels(0, 0, self.videoSize[0], self.videoSize[1], GL_RGB, GL_UNSIGNED_BYTE)
frameBuffer = numpy.frombuffer(frameBuffer, numpy.uint8)
frameBuffer = frameBuffer.reshape((self.videoSize[1], self.videoSize[0], 3))
frameBuffer = numpy.flipud(frameBuffer)
self.videoFrames += [frameBuffer]
# }}}
# {{{ saveVideo(self): XXX
def saveVideo(self):
with subprocess.Popen([
"FFmpeg.exe",
"-pix_fmt", "rgb24",
"-r", str(self.videoFps),
"-s", "x".join([str(r) for r in self.videoSize]),
"-vcodec", "rawvideo",
"-f", "rawvideo",
"-i", "-",
"-an",
"-y",
self.videoPath], stdin=subprocess.PIPE) as procObject:
for videoFrame in self.videoFrames:
procObject.stdin.write(videoFrame.tobytes())
# }}}
# {{{ __init__(self, videoPath, videoSize, videoFps=25): XXX
def __init__(self, videoPath, videoSize, videoFps=25):
self.videoFps, self.videoPath, self.videoSize = videoFps, videoPath, videoSize
self.videoFrames = []
# }}}
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -26,7 +26,6 @@ class ENNToolMiRCARTImporter(object):
PS_COLOUR_DIGIT0 = 2 PS_COLOUR_DIGIT0 = 2
PS_COLOUR_DIGIT1 = 3 PS_COLOUR_DIGIT1 = 3
# }}} # }}}
# {{{ _flipCellStateBit(self, cellState, bit): XXX # {{{ _flipCellStateBit(self, cellState, bit): XXX
def _flipCellStateBit(self, cellState, bit): def _flipCellStateBit(self, cellState, bit):
if cellState & bit: if cellState & bit:

View File

@ -2,4 +2,5 @@
Copyright (c) 2018 Lucio Andrés Illanes Albornoz <<lucio@lucioillanes.de>> Copyright (c) 2018 Lucio Andrés Illanes Albornoz <<lucio@lucioillanes.de>>
This project is licensed under the terms of the MIT licence. 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: * 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` `pip install chardet numpy Pillow PyOpenGL wxPython`
* Additionally, FFmpeg.exe must be present in the current working directory and/or %PATH%.