Example #1
0
 def __setCurrentShot(self, *args):
     shot = self.__shotsManager.shotAtTime(self._timer.time)
     if shot is None:
         self.__sceneView.setScene(None)
         self.__profiler.setScene(None)
         return
     sceneFile = currentScenesDirectory().join(shot.sceneName + SCENE_EXT)
     sc = Scene.getScene(sceneFile)
     self.__sceneView.setScene(sc)
     self.__profiler.setScene(sc)
Example #2
0
def _deserializeSceneShots(sceneName):
    sceneFile = currentScenesDirectory().join(sceneName.ensureExt(SCENE_EXT))
    xScene = parseXMLWithIncludes(sceneFile)

    for xShot in xScene:
        name = xShot.attrib['name']
        start = float(xShot.attrib['start'])
        end = float(xShot.attrib['end'])
        speed = float(xShot.attrib.get(
            'speed', 1.0))  # using get for legacy file support
        preroll = float(xShot.attrib.get('preroll', 0.0))

        curves = OrderedDict()
        textures = OrderedDict()
        for xEntry in xShot:
            if xEntry.tag.lower() == 'channel':
                curveName = xEntry.attrib['name']
                keys = []
                if xEntry.text:
                    keys = xEntry.text.split(',')
                curve = Curve()
                for i in range(0, len(keys), 8):
                    curve.addKeyWithTangents(
                        tangentBroken=int(keys[i + 6]),
                        tangentMode=int(keys[i + 7]),
                        *[float(x) for x in keys[i:i + 6]])
                curves[curveName] = curve

            if xEntry.tag.lower() == 'texture':
                textures[xEntry.attrib['name']] = FilePath(
                    xEntry.attrib['path'])

        shot = Shot(name, sceneName, start, end, curves, textures, speed,
                    preroll)
        if 'enabled' in xShot.attrib:
            shot.enabled = xShot.attrib['enabled'] == str(True)
        yield shot
Example #3
0
def run():
    shots = []
    scenes = []
    scenesDir = currentScenesDirectory()

    for scenePath in scenesDir.iter(join=True):
        if not scenePath.hasExt(SCENE_EXT):
            continue
        sceneDir = FilePath(scenePath.strip()).stripExt()
        xScene = parseXMLWithIncludes(scenePath)

        templatePath = scenesDir.join(scenesDir, xScene.attrib['template'])
        templateDir = templatePath.stripExt()
        xTemplate = Template(templatePath)

        scene = []

        for xPass in xTemplate:
            stitchIds = []
            uniforms = {}
            for xSection in xPass:
                baseDir = sceneDir
                if xSection.tag in ('global', 'shared'):
                    baseDir = templateDir
                shaderFile = baseDir.join(xSection.attrib['path']).abs()
                stitchIds.append(text.addFile(shaderFile))
                for xUniform in xSection:
                    name = xUniform.attrib['name']
                    values = [
                        float(x.strip())
                        for x in xUniform.attrib['value'].split(',')
                    ]
                    uniforms[text.addString(name)] = len(
                        values), floats.addFloats(values, name)

            programId = shaders.fromStitches(stitchIds)

            buffer = int(xPass.attrib.get('buffer', -1))
            outputs = int(xPass.attrib.get('outputs', 1))
            size = int(xPass.attrib.get('size', 0))
            width = int(xPass.attrib.get('width', size))
            height = int(xPass.attrib.get('height', size))
            factor = int(xPass.attrib.get('factor', 1))
            static = int(xPass.attrib.get('static', 0))
            is3d = int(xPass.attrib.get('is3d', 0))
            if buffer != -1:
                buffer = framebuffers.add(buffer, outputs, width, height,
                                          factor, static, is3d)

            i = 0
            key = 'input%s' % i
            inputs = []
            while key in xPass.attrib:
                v = xPass.attrib[key]
                if '.' in v:
                    a, b = v.split('.')
                else:
                    a, b = v, 0
                inputs.append((int(a), int(b)))
                i += 1
                key = 'input%s' % i

            scene.append(passes.add(programId, buffer, inputs, uniforms))

        sceneIndex = len(scenes)
        scenes.append(len(scene))
        scenes += scene

        for xShot in xScene:
            if xShot.attrib.get('enabled', 'True') == 'False':
                continue
            animations = {}
            for xChannel in xShot:
                uname = xChannel.attrib['name']
                n = uname
                x = 0
                if '.' in uname:
                    n, x = uname.rsplit('.', 1)
                    x = 'xyzw'.index(x)
                n = text.addString(n)
                if n not in animations:
                    animations[n] = []
                if not xChannel.text:
                    keyframes = []
                else:
                    keyframes = []
                    for i, v in enumerate(
                            float(v.strip())
                            for v in xChannel.text.split(',')):
                        j = i % 8
                        if j == 0 or j == 4 or j > 5:
                            continue
                        if j == 5:  # out tangent y
                            if v == float(
                                    'inf'
                            ):  # stepped tangents are implemented as out tangentY = positive infinity
                                v = 'FLT_MAX'
                        keyframes.append(v)
                    assert len(keyframes) / 4.0 == int(len(keyframes) /
                                                       4), len(keyframes)
                while len(animations[n]) <= x:
                    animations[n].append(None)
                assert animations[n][x] is None
                animations[n][x] = floats.addFloats(keyframes), len(keyframes)

            for channelStack in animations.values():
                # TODO we can not / do not check if the channelStack length matches the uniform dimensions inside the shader (e.g. are we sure we're not gonna call glUniform2f for a vec3?)
                assert None not in channelStack, 'Animation provided for multiple channels but there is one missing (Y if a vec3 or also Z if a vec4).'

            shots.append((float(xShot.attrib['start']),
                          float(xShot.attrib['end']), sceneIndex, animations))

    # sort shots by start time
    def _serializeShots(shots):
        shots.sort(key=lambda x: x[0])
        shotTimesStart = floats.addFloats(
            [x for shot in shots for x in (shot[0], shot[1])])
        yield '\n\n__forceinline int shotAtBeats(float beats, float& localBeats)\n{\n'
        if len(shots) == 1:
            yield '\tlocalBeats = beats - gFloatData[%s];\n' % shotTimesStart
            yield '\treturn 0;\n'
        else:
            yield '\tint shotTimeCursor = 0;\n'
            yield '\tdo\n\t{\n'
            yield '\t\tif(beats < gFloatData[shotTimeCursor * 2 + %s])\n\t\t{\n' % (
                shotTimesStart + 1)
            yield '\t\t\tlocalBeats = beats - gFloatData[shotTimeCursor * 2 + %s];\n' % shotTimesStart
            yield '\t\t\treturn shotTimeCursor;\n'
            yield '\t\t}\n'
            yield '\t}\n\twhile(++shotTimeCursor < %s);\n' % len(shots)
            yield '\treturn -1;\n'
        yield '}\n'

        global gShotScene
        gShotScene = ints.addInts([shot[2] for shot in shots])
        flatAnimationData = []
        animationDataPtrs = []
        for shot in shots:
            animationDataPtrs += [len(flatAnimationData), len(shot[3].keys())]
            global gAnimEntriesMax
            gAnimEntriesMax = max(gAnimEntriesMax, len(shot[3].keys()))
            for uniformStringId in shot[3]:
                animationData = shot[3][uniformStringId]
                flatAnimationData += [uniformStringId, len(animationData)]
                for pair in animationData:
                    flatAnimationData += pair
                flatAnimationData += [0] * (2 * (4 - len(animationData)))

        global gShotAnimationDataIds
        gShotAnimationDataIds = ints.addInts(animationDataPtrs)
        global gShotUniformData
        gShotUniformData = ints.addInts(flatAnimationData)

    def _serializeAll(scenes, shots):
        buffer = list(_serializeShots(shots))
        for serializable in (text, floats):
            for ln in serializable.serialize():
                yield ln
        buffer2 = []
        for serializable in (shaders, framebuffers, passes):
            buffer2 += list(serializable.serialize())
        global gScenePassIds
        gScenePassIds = ints.addInts(scenes)
        for ln in ints.serialize():
            yield ln
        for ln in buffer2:
            yield ln
        for ln in buffer:
            yield ln

    data = [''.join(_serializeAll(scenes, shots))]
    data.append(
        """\n\n__forceinline float evalCurve(const float* data, int numFloats, float beats)
{
\tif(numFloats == 4 || beats <= data[1]) // 1 key or evaluating before first frame
\t\treturn data[2];

\t// Find index of first key that has a bigger time than our current time
\t// if none, this will be the index of the last key.
\tint keyValueCount = numFloats;
\tint rightKeyIndex = 4;
\twhile (rightKeyIndex < keyValueCount - 4 && data[rightKeyIndex + 1] < beats)
\t\trightKeyIndex += 4;

\t// Clamp our sampling time to our range
\tfloat sampleTime = (beats > data[rightKeyIndex + 1]) ? data[rightKeyIndex + 1] : beats;

\t// Retrieve our spline points
\tfloat y0 = data[rightKeyIndex - 2];
\tfloat y1 = data[rightKeyIndex - 1]; 
\t// handle stepped tangents
\tif(y1 == FLT_MAX) return y0;
\tfloat y2 = data[rightKeyIndex];
\tfloat y3 = data[rightKeyIndex + 2];

\tfloat dy = y3 - y0;
\tfloat c0 = y1 + y2 - dy - dy;
\tfloat c1 = dy + dy + dy - y1 - y1 - y2;
\tfloat c2 = y1;
\tfloat c3 = y0;

\t// Determine factor
\tfloat dt = data[rightKeyIndex + 1] - data[rightKeyIndex - 3];
\tfloat t = (sampleTime - data[rightKeyIndex - 3]) / dt;

\treturn t * (t * (t * c0 + c1) + c2) + c3;
}

#define gAnimEntriesMax %s
#define gShotAnimationDataIds %s
#define gShotScene %s
#define gScenePassIds %s
#define gPassProgramsAndTargets %s
#define gShotUniformData %s
#define gFrameBufferData %s
#define gFrameBufferBlockSize %s
#define gProgramCount %s
""" % (gAnimEntriesMax, gShotAnimationDataIds, gShotScene, gScenePassIds,
       gPassProgramsAndTargets, gShotUniformData, gFrameBufferData,
       FrameBufferPool.BLOCK_SIZE, len(shaders.offsets)))

    dst = FilePath(__file__).abs().parent().parent().join(
        'Player', 'generated.hpp')
    with dst.edit() as fh:
        fh.write(''.join(data))
Example #4
0
    def __record(self):
        diag = QDialog()
        fId = gSettings.value('RecordFPS', 2)
        rId = gSettings.value('RecordResolution', 3)
        layout = QGridLayout()
        diag.setLayout(layout)
        layout.addWidget(QLabel('FPS: '), 0, 0)
        fps = QComboBox()
        fps.addItems(['12', '24', '30', '48', '60', '120'])
        fps.setCurrentIndex(fId)
        layout.addWidget(fps, 0, 1)
        layout.addWidget(QLabel('Vertical resolution: '), 1, 0)
        resolution = QComboBox()
        resolution.addItems(['144', '288', '360', '720', '1080', '2160'])
        resolution.setCurrentIndex(rId)
        layout.addWidget(resolution, 1, 1)
        ok = QPushButton('Ok')
        ok.clicked.connect(diag.accept)
        cancel = QPushButton('Cancel')
        cancel.clicked.connect(diag.reject)
        layout.addWidget(ok, 2, 0)
        layout.addWidget(cancel, 2, 1)
        diag.exec_()
        if diag.result() != QDialog.Accepted:
            return
        gSettings.setValue('RecordFPS', fps.currentIndex())
        gSettings.setValue('RecordResolution', resolution.currentIndex())

        FPS = int(fps.currentText())
        HEIGHT = int(resolution.currentText())
        WIDTH = (HEIGHT * 16) / 9
        FMT = 'jpg'

        data = (ctypes.c_ubyte * (WIDTH * HEIGHT * 3))()  # alloc buffer once
        flooredStart = self._timer.secondsToBeats(
            int(self._timer.beatsToSeconds(self._timer.start) * FPS) /
            float(FPS))
        duration = self._timer.beatsToSeconds(self._timer.end - flooredStart)

        captureDir = currentProjectDirectory().join('capture')
        captureDir.ensureExists(isFolder=True)

        progress = QProgressDialog(self)
        progress.setMaximum(int(duration * FPS))
        prevFrame = 0
        for frame in range(int(duration * FPS)):
            deltaTime = (frame - prevFrame) / float(FPS)
            prevFrame = frame
            progress.setValue(frame)
            QApplication.processEvents()
            if progress.wasCanceled():
                break
            beats = flooredStart + self._timer.secondsToBeats(
                frame / float(FPS))

            shot = self.__shotsManager.shotAtTime(beats)
            if shot is None:
                continue
            sceneFile = currentScenesDirectory().join(
                shot.sceneName).ensureExt(SCENE_EXT)
            scene = Scene.getScene(sceneFile)
            scene.setSize(WIDTH, HEIGHT)

            uniforms = self.__shotsManager.evaluate(beats)
            textureUniforms = self.__shotsManager.additionalTextures(beats)
            self.__sceneView._cameraInput.setData(
                *(uniforms['uOrigin'] + uniforms['uAngles'])
            )  # feed animation into camera so animationprocessor can read it again
            cameraData = self.__sceneView._cameraInput.data()

            modifier = currentProjectDirectory().join('animationprocessor.py')
            if modifier.exists():
                execfile(str(modifier), globals(), locals())

            for name in self.__sceneView._textures:
                uniforms[name] = self.__sceneView._textures[name]._id

            scene.drawToScreen(self._timer.beatsToSeconds(beats), beats,
                               uniforms, (0, 0, WIDTH, HEIGHT),
                               textureUniforms)
            scene.colorBuffers[-1][0].use()

            from OpenGL.GL import glGetTexImage, GL_TEXTURE_2D, GL_RGB, GL_UNSIGNED_BYTE
            glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, data)

            captureDir = currentProjectDirectory().join('capture')
            QImage(data, WIDTH, HEIGHT, QImage.Format_RGB888).mirrored(
                False, True
            ).save(
                captureDir.join(
                    'dump_%s_%05d.%s' %
                    (FPS,
                     int(self._timer.beatsToSeconds(self._timer.start) * FPS) +
                     frame, FMT)))
        progress.close()

        convertCaptureDir = currentProjectDirectory().join('convertcapture')
        convertCaptureDir.ensureExists(isFolder=True)

        with convertCaptureDir.join('convert.bat').edit() as fh:
            start = ''
            start2 = ''
            if int(self._timer.start * FPS) > 0:
                start = '-start_number {} '.format(
                    int(self._timer.beatsToSeconds(self._timer.start) * FPS))
                start2 = '-vframes {} '.format(
                    int(
                        self._timer.beatsToSeconds(self._timer.end -
                                                   self._timer.start) * FPS))

            fh.write(
                'cd "../capture"\n"{}" -framerate {} {}-i dump_{}_%%05d.{} {}-c:v libx264 -r {} -pix_fmt yuv420p "../convertcapture/output.mp4"'
                .format(FFMPEG_PATH, FPS, start, FPS, FMT, start2, FPS))
        with convertCaptureDir.join('convertGif.bat').edit() as fh:
            start = ''
            start2 = ''
            iln = ''
            if int(self._timer.start * FPS) > 0:
                start = '-start_number {} '.format(
                    int(self._timer.beatsToSeconds(self._timer.start) * FPS))
                start2 = '-vframes {} '.format(
                    int(
                        self._timer.beatsToSeconds(self._timer.end -
                                                   self._timer.start) * FPS))
                iln = '-t {:03f} '.format(
                    self._timer.beatsToSeconds(self._timer.end -
                                               self._timer.start))
            fh.write(
                'REM File format is actually %5d but in a .bat file we need to escape % or something, so you can\'t copy paste this into a cmd prompt without fixing up the %%05d to be %5d.\n'
            )
            fh.write(
                'cd "../capture"\n"{}" -framerate {} {}{}-i dump_{}_%%05d.{} -vf "fps={},scale={}:-1:flags=lanczos,palettegen" palette.png\n'
                .format(FFMPEG_PATH, FPS, start, iln, FPS, FMT, FPS, HEIGHT))
            fh.write(
                '"{}" -framerate {} {}-i dump_{}_%%05d.{} -i "palette.png" -filter_complex "fps=12,scale=360:-1:flags=lanczos[x];[x][1:v]paletteuse" {}-r {} "../convertcapture/output.gif"'
                .format(FFMPEG_PATH, FPS, start, FPS, FMT, start2, FPS))

        sound = self.timeSlider.soundtrackPath()
        if not sound:
            return

        with convertCaptureDir.join('merge.bat').edit() as fh:
            startSeconds = self._timer.beatsToSeconds(self._timer.start)
            fh.write(
                '{} -i output.mp4 -itsoffset {} -i "{}" -vcodec copy -shortest merged.mp4'
                .format(FFMPEG_PATH, -startSeconds, sound))
Example #5
0
def _saveSceneShots(sceneName, shots):
    sceneFile = currentScenesDirectory().join(sceneName.ensureExt(SCENE_EXT))
    xScene = parseXMLWithIncludes(sceneFile)

    # save user camera position per scene
    userFile = currentProjectFilePath().ensureExt('user')
    if userFile.exists():
        xUser = parseXMLWithIncludes(userFile)
    else:
        xUser = cElementTree.Element('user')
    if sceneFile in Scene.cache:
        cameraData = Scene.cache[sceneFile].cameraData()
        if cameraData:
            for xSub in xUser:
                if xSub.tag == 'scene' and xSub.attrib['name'] == sceneName:
                    xSub.attrib['camera'] = ','.join(
                        [str(x) for x in cameraData])
                    break
            else:
                cElementTree.SubElement(
                    xUser, 'scene', {
                        'name': sceneName,
                        'camera': ','.join([str(x) for x in cameraData])
                    })

    with userFile.edit() as fh:
        fh.write(toPrettyXml(xUser))

    # remove old shots
    r = []
    for xShot in xScene:
        r.append(xShot)
    for s in r:
        xScene.remove(s)

    targets = []
    for shot in shots:
        if shot.sceneName == sceneName:
            targets.append(shot)

    for shot in targets:
        xShot = cElementTree.SubElement(
            xScene, 'Shot', {
                'name': shot.name,
                'scene': sceneName,
                'start': str(shot.start),
                'end': str(shot.end),
                'enabled': str(shot.enabled),
                'speed': str(shot.speed),
                'preroll': str(shot.preroll)
            })
        for curveName in shot.curves:
            xChannel = cElementTree.SubElement(xShot, 'Channel', {
                'name': curveName,
                'mode': 'hermite'
            })
            data = []
            for key in shot.curves[curveName]:
                data.append(str(key.inTangent.x))
                data.append(str(key.inTangent.y))
                data.append(str(key.point().x))
                data.append(str(key.point().y))
                data.append(str(key.outTangent.x))
                data.append(str(key.outTangent.y))
                data.append(str(int(key.tangentBroken)))
                data.append(str(key.tangentMode))
            xChannel.text = ','.join(data)
        for texName in shot.textures:
            cElementTree.SubElement(xShot, 'Texture', {
                'name': texName,
                'path': shot.textures[texName]
            })

    with sceneFile.edit() as fh:
        fh.write(toPrettyXml(xScene))