2010-08-27 12:28:02 -04:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
2011-12-17 21:25:15 -05:00
|
|
|
# cuburn, one of a surprisingly large number of ports of the fractal flame
|
2010-08-27 12:28:02 -04:00
|
|
|
# algorithm to NVIDIA GPUs.
|
|
|
|
#
|
2011-12-17 21:25:15 -05:00
|
|
|
# This one is copyright 2010-2011, Steven Robertson <steven@strobe.cc>
|
|
|
|
# and Eric Reckase <e.reckase@gmail.com>.
|
2010-08-27 12:28:02 -04:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License version 2 or later
|
|
|
|
# as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
2011-10-16 13:51:04 -04:00
|
|
|
import time
|
2011-12-17 21:25:15 -05:00
|
|
|
import json
|
|
|
|
import warnings
|
2011-10-12 14:09:13 -04:00
|
|
|
import argparse
|
2011-06-13 23:20:18 -04:00
|
|
|
from subprocess import Popen
|
2011-10-15 22:22:43 -04:00
|
|
|
from itertools import ifilter
|
2010-08-27 12:28:02 -04:00
|
|
|
|
|
|
|
import numpy as np
|
2011-10-12 14:09:13 -04:00
|
|
|
import Image
|
2011-05-02 15:30:14 -04:00
|
|
|
import scipy
|
2011-12-17 21:25:15 -05:00
|
|
|
import pycuda.driver as cuda
|
2011-05-03 17:12:12 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
from cuburn import genome, render
|
2011-05-03 13:02:15 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
profiles = {
|
|
|
|
'1080p': dict(fps=24, width=1920, height=1080, quality=3000, skip=0),
|
|
|
|
'720p': dict(fps=24, width=1280, height=720, quality=2500, skip=0),
|
|
|
|
'preview': dict(fps=24, width=640, height=360, quality=800, skip=1)
|
|
|
|
}
|
2011-10-12 14:09:13 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
def save(rframe):
|
|
|
|
noalpha = rframe.buf[:,:,:3]
|
2011-10-12 14:09:13 -04:00
|
|
|
img = scipy.misc.toimage(noalpha, cmin=0, cmax=1)
|
2011-12-17 21:25:15 -05:00
|
|
|
img.save(rframe.idx, quality=95)
|
|
|
|
print rframe.idx, rframe.gpu_time
|
|
|
|
|
|
|
|
def main(args, prof):
|
|
|
|
import pycuda.autoinit
|
|
|
|
|
|
|
|
gnm_str = args.flame.read()
|
|
|
|
if '<' in gnm_str[:10]:
|
|
|
|
flames = genome.XMLGenomeParser.parse(gnm_str)
|
|
|
|
if len(flames) != 1:
|
|
|
|
warnings.warn('%d flames in file, only using one.' % len(flames))
|
|
|
|
gnm = genome.convert_flame(flames[0])
|
2011-10-03 17:10:38 -04:00
|
|
|
else:
|
2011-12-17 21:25:15 -05:00
|
|
|
gnm = json.loads(gnm_str)
|
|
|
|
gnm = genome.Genome(gnm)
|
|
|
|
err, times = gnm.set_profile(prof)
|
|
|
|
|
|
|
|
anim = render.Renderer()
|
|
|
|
anim.compile(gnm, keep=args.keep)
|
|
|
|
anim.load(gnm)
|
|
|
|
|
|
|
|
basename = os.path.basename(args.flame.name).rsplit('.', 1)[0] + '_'
|
|
|
|
if args.flame.name == '-':
|
|
|
|
basename = ''
|
2012-01-03 11:24:20 -05:00
|
|
|
if args.name is not None:
|
|
|
|
basename = args.name
|
|
|
|
prefix = os.path.join(args.dir, basename)
|
2011-12-17 21:25:15 -05:00
|
|
|
frames = [(prefix + '%05d.jpg' % (i+1), t) for i, t in enumerate(times)]
|
|
|
|
if args.end:
|
|
|
|
frames = frames[:args.end]
|
|
|
|
frames = frames[args.start::prof['skip']+1]
|
2011-10-15 22:21:49 -04:00
|
|
|
if args.resume:
|
2012-01-03 11:24:42 -05:00
|
|
|
m = 0
|
|
|
|
if args.flame.name != '-':
|
|
|
|
m = os.path.getmtime(args.flame.name)
|
|
|
|
frames = (f for f in frames
|
|
|
|
if not os.path.isfile(f[0]) or m > os.path.getmtime(f[0]))
|
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
w, h = prof['width'], prof['height']
|
|
|
|
gen = anim.render(gnm, frames, w, h)
|
2011-10-12 14:09:13 -04:00
|
|
|
|
|
|
|
if args.gfx:
|
|
|
|
import pyglet
|
2011-12-17 21:25:15 -05:00
|
|
|
window = pyglet.window.Window(w, h)
|
|
|
|
image = pyglet.image.CheckerImagePattern().create_image(w, h)
|
2011-10-12 14:09:13 -04:00
|
|
|
label = pyglet.text.Label('Rendering first frame', x=5, y=5,
|
2011-12-28 15:39:33 -05:00
|
|
|
font_size=16, bold=True)
|
2011-10-12 14:09:13 -04:00
|
|
|
|
|
|
|
@window.event
|
|
|
|
def on_draw():
|
|
|
|
window.clear()
|
|
|
|
image.texture.blit(0, 0)
|
|
|
|
label.draw()
|
|
|
|
|
|
|
|
@window.event
|
|
|
|
def on_key_press(sym, mod):
|
|
|
|
if sym == pyglet.window.key.Q:
|
|
|
|
pyglet.app.exit()
|
|
|
|
|
|
|
|
@window.event
|
|
|
|
def on_mouse_motion(x, y, dx, dy):
|
|
|
|
pass
|
|
|
|
|
2011-10-16 13:51:04 -04:00
|
|
|
last_time = [time.time()]
|
|
|
|
|
2011-10-12 14:09:13 -04:00
|
|
|
def poll(dt):
|
2011-12-17 21:25:15 -05:00
|
|
|
out = next(gen, False)
|
2011-10-12 14:09:13 -04:00
|
|
|
if out is False:
|
2011-10-16 13:52:27 -04:00
|
|
|
if args.nopause:
|
|
|
|
pyglet.app.exit()
|
|
|
|
else:
|
|
|
|
label.text = "Done. ('q' to quit)"
|
|
|
|
pyglet.clock.unschedule(poll)
|
2011-10-12 14:09:13 -04:00
|
|
|
elif out is not None:
|
2011-10-16 13:51:04 -04:00
|
|
|
real_dt = time.time() - last_time[0]
|
|
|
|
last_time[0] = time.time()
|
2011-12-17 21:25:15 -05:00
|
|
|
save(out)
|
|
|
|
imgbuf = np.uint8(out.buf.flatten() * 255)
|
|
|
|
image.set_data('RGBA', -w*4, imgbuf.tostring())
|
|
|
|
label.text = '%s (%g fps)' % (out.idx, 1./real_dt)
|
2011-10-15 22:22:43 -04:00
|
|
|
else:
|
|
|
|
label.text += '.'
|
2011-12-17 21:25:15 -05:00
|
|
|
if args.sync:
|
|
|
|
cuda.Context.synchronize()
|
2011-10-12 14:09:13 -04:00
|
|
|
|
|
|
|
pyglet.clock.set_fps_limit(30)
|
|
|
|
pyglet.clock.schedule_interval(poll, 1/30.)
|
|
|
|
pyglet.app.run()
|
|
|
|
else:
|
2011-12-17 21:25:15 -05:00
|
|
|
for out in gen:
|
|
|
|
save(out)
|
|
|
|
if args.sync:
|
|
|
|
cuda.Context.synchronize()
|
2010-08-27 12:28:02 -04:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2011-10-12 14:09:13 -04:00
|
|
|
parser = argparse.ArgumentParser(description='Render fractal flames.')
|
|
|
|
|
2011-10-15 22:20:00 -04:00
|
|
|
parser.add_argument('flame', metavar='FILE', type=argparse.FileType(),
|
2011-10-12 14:09:13 -04:00
|
|
|
help="Path to genome file ('-' for stdin)")
|
|
|
|
parser.add_argument('-g', action='store_true', dest='gfx',
|
|
|
|
help="Show output in OpenGL window")
|
|
|
|
parser.add_argument('-n', metavar='NAME', type=str, dest='name',
|
|
|
|
help="Prefix to use when saving files (default is basename of input)")
|
|
|
|
parser.add_argument('-o', metavar='DIR', type=str, dest='dir',
|
|
|
|
help="Output directory", default='.')
|
2011-10-15 22:21:49 -04:00
|
|
|
parser.add_argument('--resume', action='store_true', dest='resume',
|
2012-01-03 11:24:42 -05:00
|
|
|
help="Don't overwrite output files that are newer than the input")
|
2011-10-16 13:52:27 -04:00
|
|
|
parser.add_argument('--nopause', action='store_true',
|
2011-12-17 21:25:15 -05:00
|
|
|
help="Don't pause after rendering the last frame when previewing")
|
2011-10-12 14:09:13 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
parser.add_argument('--keep', action='store_true', dest='keep',
|
2011-10-12 14:09:13 -04:00
|
|
|
help='Keep compilation directory (disables kernel caching)')
|
2011-12-17 21:25:15 -05:00
|
|
|
parser.add_argument('--sync', action='store_true', dest='sync',
|
2011-10-13 07:53:55 -04:00
|
|
|
help='Use synchronous launches whenever possible')
|
2011-10-12 14:09:13 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
parser.add_argument('--duration', type=float, metavar='TIME',
|
|
|
|
help="Set base duration in seconds (30)", default=30)
|
|
|
|
parser.add_argument('--start', metavar='FRAME_NO', type=int,
|
|
|
|
default=0, help="First frame to render (inclusive)")
|
|
|
|
parser.add_argument('--end', metavar='FRAME_NO', type=int,
|
|
|
|
help="Last frame to render (exclusive, negative OK)")
|
|
|
|
|
|
|
|
prof = parser.add_argument_group('Profile options')
|
|
|
|
prof.add_argument('-p', dest='prof', choices=profiles.keys(),
|
|
|
|
default='preview', help='Set profile, specifying defaults for all '
|
|
|
|
'options below. (default: "preview")')
|
|
|
|
prof.add_argument('--skip', dest='skip', metavar='N', type=int,
|
|
|
|
help="Skip N frames between each rendered frame")
|
|
|
|
prof.add_argument('--quality', type=int, metavar='SPP',
|
|
|
|
help="Set base samples per pixel")
|
|
|
|
prof.add_argument('--fps', type=float, dest='fps',
|
2011-12-18 21:41:39 -05:00
|
|
|
help="Set frames per second (24)")
|
2011-12-17 21:25:15 -05:00
|
|
|
prof.add_argument('--width', type=int, metavar='PX')
|
|
|
|
prof.add_argument('--height', type=int, metavar='PX')
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
prof = dict(profiles[args.prof])
|
|
|
|
for k in prof:
|
|
|
|
if getattr(args, k) is not None:
|
|
|
|
prof[k] = getattr(args, k)
|
|
|
|
prof['duration'] = args.duration
|
2010-08-27 12:28:02 -04:00
|
|
|
|
2011-12-17 21:25:15 -05:00
|
|
|
main(args, prof)
|