#!/usr/bin/env lua -- -- CS5600 University of Utah -- Charles McGarvey -- mcgarvey@eng.utah.edu -- -- This program automates the process of creating simple fly-by animations. -- The rasters are saved in the `frames' directory, and if ffmpeg is -- installed, the frames will also be combined into a video file. This script -- must be called from the same directory where the rasterize program is. -- Set the number of frames to be rendered for the animation. local frames = 360 -- Define the code to calculate where the camera is, in world coordinates. local eye = function(frame) -- just rotate around the center of the scene on the XZ plane local center = vec_new(0, 1, 0) local distance = 4 local start = math.pi local t = start + 2 * math.pi * frame / frames local v = vec_new(math.cos(t), 0, math.sin(t)) return vec_add(vec_scale(v, distance), center) end -- Define the code to calculate where the focal point of the scene is. local look = function(frame) -- keep the camera focused on the buddha return vec_new(2, 1, 2) end -- Define the actual objects of the scene that will be rendered, in the -- extended u3d format. local scene = [[ L 0 1000000 0 1 1 1 1 1 1 L 0 0 1000000 1 1 1 1 1 1 g ak47.obj c 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 M 0.9 0.9 0.9 128 g dragon.raw M 0.3 0.3 0.3 5 c 0.7 0.3 0.2 0.8 0.2 0.1 0.9 0.2 0.2 t -2 -1 -2 s 2 2 2 g budda.raw c 0.1 0.2 0.7 0.1 0.3 0.9 0.2 0.1 0.8 t 2 -1.5 2 s 10 10 10 g bunny.raw M 0.2 0.2 0.2 1 c 0.9 0.8 0.9 0.8 0.7 0.9 0.9 0.8 0.7 t -2 -1 2 s 10 10 10 ]] -- Set the number of samples for supersampling. If this is set to a value of -- two or greater, each frame will be rendered larger by the factor specified. -- ImageMagick is used to scale the frames back down to size; if ImageMagick -- is not installed, the resulting frames will end up bigger than they should -- be and the aliasing problem will not be fixed. local supersample = 2 -- Set the width and height of the viewport. local size = {w = 640, h = 480} -- Set the number of concurrent renderings (for multi-core machines). local jobs = 6 -- Set the options to be passed directly to ffmpeg. local ffmpeg_opts = "-b 1024k" -- end of configuration --------------------------------------------------------------------------- function vec_new(x, y, z) return {x = x, y = y, z = z} end function vec_add(a, b) return vec_new(a.x + b.x, a.y + b.y, a.z + b.z) end function vec_scale(v, s) return vec_new(v.x * s, v.y * s, v.z * s) end local fmt = string.format local write = function(...) io.write(fmt(...)) io.flush() end local run = function(...) return os.execute(fmt(...)) end function render(i) while i do local w, h = size.w, size.h if 1 < supersample then w = w * supersample h = h * supersample end local filename = fmt("frames/anim%04d.bmp", i) local command = fmt("./rasterize -o %s >/dev/null", filename) local out = io.popen(command, "w") local e = eye(i) local l = look(i) write("\27[80D\27[2Kframe\t %4d / %d", i, frames) out:write(fmt([[ U3 %d %d %f %f %f %f %f %f 0 1 0 1.57 %f 0.1 1000 %s X ]], w, h, e.x, e.y, e.z, l.x, l.y, l.z, size.w/size.h, scene)) i = coroutine.yield() out:close() if 1 < supersample then run("convert %s -scale %dx%d %s", filename, size.w, size.h, filename) end end end function renderings() local t = {} for i = 1,jobs do table.insert(t, coroutine.wrap(render)) end local iterations = frames + jobs local frame = 0 return function() frame = frame + 1 if frame <= iterations then local i = frame if frames < i then i = null end return t[1 + frame % jobs], i end end end print("Animating scene...") run("rm -rf frames && mkdir frames >/dev/null 2>&1") for render,frame in renderings() do render(frame) end print() if run("ffmpeg -i frames/anim%%04d.bmp %s -y -an scene.avi", ffmpeg_opts) == 0 then print("Animation written to scene.avi.") end