コード例 #1
0
    void Render(ExampleClock& clock) {
        if(long(clock.Now().Seconds()) % 4 == 0) {
            status += clock.Interval().Seconds();
        } else if(status != double(long(status))) {
            if(status - double(long(status)) < 0.5)
                status = double(long(status));
            else
                status = 1.0 + double(long(status));
        }

        gl.Clear().ColorBuffer().DepthBuffer();

        point_prog.status = GLfloat(0.5 - 0.5 * CosineWave(status * 0.5));

        CamMatrixf camera = CamMatrixf::Orbiting(
          Vec3f(),
          5.5f,
          FullCircles(clock.Now().Seconds() / 19.0),
          Degrees(45 + SineWave(clock.Now().Seconds() / 15.0) * 40));
        point_prog.camera_matrix.Set(camera);

        point_prog.model_matrix.Set(
          ModelMatrixf::RotationX(RightAngles(status)));
        shape.Draw();
    }
コード例 #2
0
	void Render(ExampleClock& clock)
	{
		positions.clear();
		ages.clear();

		// update the emitters and get the particle data
		for(auto i=emitters.begin(), e=emitters.end(); i!=e; ++i)
		{
			i->Update(clock);
			i->Upload(positions, ages);
		}
		assert(positions.size() == ages.size());

		// make a camera matrix
		auto cameraMatrix = CamMatrixf::Orbiting(
			Vec3f(),
			38.0 - SineWave(clock.Now().Seconds() / 6.0) * 17.0,
			FullCircles(clock.Now().Seconds() * 0.1),
			Degrees(SineWave(clock.Now().Seconds() / 20.0) * 60)
		);

		std::vector<float> depths(positions.size());
		std::vector<GLuint> indices(positions.size());
		// calculate the depths of the particles
		for(GLuint i=0, n=positions.size(); i!=n; ++i)
		{
			depths[i] = (cameraMatrix * Vec4f(positions[i], 1.0)).z();
			indices[i] = i;
		}

		// sort the indices by the depths
		std::sort(
			indices.begin(),
			indices.end(),
			[&depths](GLuint i, GLuint j)
			{
				return depths[i] < depths[j];
			}
		);

		// upload the particle positions
		pos_buf.Bind(Buffer::Target::Array);
		Buffer::Data(Buffer::Target::Array, positions, BufferUsage::DynamicDraw);
		// upload the particle ages
		age_buf.Bind(Buffer::Target::Array);
		Buffer::Data(Buffer::Target::Array, ages, BufferUsage::DynamicDraw);

		gl.Clear().ColorBuffer().DepthBuffer();
		camera_matrix.Set(cameraMatrix);
		// use the indices to draw the particles
		gl.DrawElements(
			PrimitiveType::Points,
			indices.size(),
			indices.data()
		);
	}
コード例 #3
0
void run_loop(
	std::unique_ptr<Example>& example,
	GLuint width,
	GLuint height
)
{
	GLuint mouse_x = width / 2;
	GLuint mouse_y = height / 2;
	os::steady_clock os_clock;
	ExampleClock clock;
	while(true)
	{
		clock.Update(os_clock.seconds());
		if(!example->Continue(clock)) break;
		example->Render(clock);

		glfwSwapBuffers();

		int new_x, new_y;
		glfwGetWindowSize(&new_x, &new_y);
		if((int(width) != new_x) || (int(height) != new_y))
		{
			if(new_x <= 0) new_x = 1;
			if(new_y <= 0) new_y = 1;
			width = GLuint(new_x);
			height = GLuint(new_y);
			example->Reshape(width, height);
		}

		glfwGetMousePos(&new_x, &new_y);
		if((int(mouse_x) != new_x) || (int(mouse_y) != new_y))
		{
			if(new_x <= 0) new_x = 1;
			if(new_y <= 0) new_y = 1;
			mouse_x = GLuint(new_x);
			mouse_y = GLuint(new_y);
			example->MouseMove(
				mouse_x,
				height-
				mouse_y,
				width,
				height
			);
		}

		if(glfwGetKey(GLFW_KEY_ESC))
		{
			glfwCloseWindow();
			break;
		}
		if(!glfwGetWindowParam(GLFW_OPENED))
			break;
	}
}
コード例 #4
0
ファイル: glut_main.cpp プロジェクト: James-Z/oglplus
	void Display(void)
	{
		_clock.Update(_os_clock.seconds());
		double frame_time = _clock.Now().Seconds();
		_frame_no++;

		GLuint primitives_per_frame = 0;
		if(_primitive_query)
		{
			try
			{
				auto query_exec = _primitive_query->Execute(
					Query::Target::PrimitivesGenerated,
					primitives_per_frame
				);
				_example->Render(_clock);
				glutSwapBuffers();
			}
			catch(Error&)
			{
				_primitive_query.reset();
			}
		}
		else
		{
			_example->Render(_clock);
			glutSwapBuffers();
		}
		_prim_count += double(primitives_per_frame);

		const double fps_interval = 10.0;
		const double this_interval = frame_time - _fps_time;
		if(this_interval >= fps_interval)
		{
			_fps_time = frame_time;
			std::cout.width(5);
			std::cout.precision(3);
			std::cout << _frame_no << " frames in "
				<< std::fixed << this_interval << " seconds = "
				<< std::fixed << _frame_no/this_interval << " FPS; "
				<< std::scientific << _prim_count/this_interval << " PPS; "
				<< std::scientific << _prim_count/_frame_no << " PPF; "
				<< std::endl;
			_frame_no = 0;
			_prim_count = 0.0;
		}

		if(!_example->Continue(_clock))
		{
			Quit();
		}
	}
コード例 #5
0
ファイル: sdl_main.cpp プロジェクト: BrainlessLabsInc/oglplus
void run_loop(
	std::unique_ptr<Example>& example,
	GLuint width,
	GLuint height
)
{
	os::steady_clock os_clock;
	ExampleClock clock;
	bool done = false;

	while(!done)
	{
		clock.Update(os_clock.seconds());
		if(!example->Continue(clock)) break;
		example->Render(clock);

		SDL_GL_SwapBuffers();

		SDL_Event event;
		while(SDL_PollEvent(&event))
		{
			if(event.type == SDL_MOUSEMOTION)
			{
				example->MouseMove(
					event.motion.x,
					height-
					event.motion.y,
					width,
					height
				);
			}
			else if(event.type == SDL_KEYUP)
			{
				if(event.key.keysym.sym == SDLK_ESCAPE)
					done = true;
			}
			else if(event.type == SDL_VIDEORESIZE)
			{
				width = event.resize.w;
				height = event.resize.h;
				example->Reshape(width, height);
			}
			else if(event.type == SDL_QUIT)
			{
				done = true;
			}
		}
	}
}
コード例 #6
0
ファイル: glut_main.cpp プロジェクト: James-Z/oglplus
	SingleExample(
		GLuint width,
		GLuint height,
		const ExampleParams& params,
		const char* screenshot_path
	): _fps_time(0.0)
	 , _prim_count(0.0)
	 , _frame_no(0)
	 , _width(width)
	 , _height(height)
	 , _example(makeExample(params))
	 , _primitive_query(new Query())
	 , _screenshot_path(screenshot_path)
	{
		assert(!SingleInstance());
		SingleInstance() = this;

		assert(_example);
		_example->Reshape(width, height);
		_example->MouseMove(width/2, height/2, width, height);
		_os_clock.reset();

		if(_screenshot_path)
			_clock.Update(_example->HeatUpTime());
	}
コード例 #7
0
	// Updates the emitter
	// Changes its position, emits new particles
	void Update(ExampleClock& clock)
	{
		assert(positions.size() == directions.size());
		assert(positions.size() == ages.size());
		assert(positions.size() == ids.size());

		double time_diff = clock.Interval().Seconds();
		double drag = 0.1 * time_diff;
		if(drag > 1.0) drag = 1.0;

		// go through the existing particles
		for(std::size_t i=0, n=positions.size(); i!=n; ++i)
		{
			// update the age
			ages[i] += time_diff / lifetime;
			// if the particle is "too old"
			if(ages[i] > 1.0)
			{
				// try to spawn a new one in its place
				SpawnParticle(
					clock.Now().Seconds(),
					positions[i],
					directions[i],
					ages[i],
					ids[i]
				);
			}
			else
			{
				// otherwise just update its motion
				directions[i] *= (1.0 - drag);
				positions[i] += directions[i] * time_diff;
			}
		}
		Vec3f position;
		Vec3f direction;
		float age;
		int id;
		// spawn new particles if necessary
		while(SpawnParticle(clock.Now().Seconds(), position, direction, age, id))
		{
			positions.push_back(position);
			directions.push_back(direction);
			ages.push_back(age);
			ids.push_back(id);
		}
	}
コード例 #8
0
ファイル: egl_main.cpp プロジェクト: Just-D/oglplus
void make_screenshot(
	eglplus::Surface& surface,
	std::unique_ptr<Example>& example,
	ExampleClock& clock,
	const ExampleOptions& opts
)
{
	double s = example->HeatUpTime();
	double t = example->ScreenshotTime();
	double dt = example->FrameTime();

	clock.Update(s);

	glEnable(GL_MULTISAMPLE);

	// heat-up
	while(s < t)
	{
		s += dt;
		clock.Update(s);

		unsigned part_no = 0;
		double comp = 0.0;
		do
		{
			comp = example->RenderPart(part_no++, clock);
		}
		while(comp < 1.0);
		if(s < t) surface.SwapBuffers();
	}
	glFinish();
	//save it to a file
	std::vector<char> pixels(opts.width * opts.height * 3);
	glReadPixels(
		0, 0,
		opts.width,
		opts.height,
		GL_RGB,
		GL_UNSIGNED_BYTE,
		pixels.data()
	);
	std::ofstream output(opts.screenshot_path);
	output.write(pixels.data(), pixels.size());
	surface.SwapBuffers();
}
コード例 #9
0
ファイル: glx_main.cpp プロジェクト: detunized/oglplus
void make_screenshot(
	const x11::Display& display,
	const x11::Window& win,
	const glx::Context& ctx,
	std::unique_ptr<Example>& example,
	ExampleClock& clock,
	GLuint width,
	GLuint height,
	const char* screenshot_path
)
{
	XEvent event;

	double s = example->HeatUpTime();
	double t = example->ScreenshotTime();
	double dt = example->FrameTime();

	clock.Update(s);

	// heat-up
	while(true)
	{
		while(display.NextEvent(event));
		s += dt;
		clock.Update(s);
		example->Render(clock);
		if(s < t) ctx.SwapBuffers(win);
		else break;
	}
	while(display.NextEvent(event));
	glFinish();
	//save it to a file
	std::vector<char> pixels(width * height * 3);
	glReadPixels(
		0, 0,
		width,
		height,
		GL_RGB,
		GL_UNSIGNED_BYTE,
		pixels.data()
	);
	std::ofstream output(screenshot_path);
	output.write(pixels.data(), pixels.size());
	ctx.SwapBuffers(win);
}
コード例 #10
0
ファイル: glut_main.cpp プロジェクト: James-Z/oglplus
	void Screenshot(void)
	{
		_example->Render(_clock);
		if(_clock.Now().Seconds() >= _example->ScreenshotTime())
		{
			glFinish();
			std::vector<char> pixels(_width * _height * 3);
			glReadPixels(
				0, 0,
				_width,
				_height,
				GL_RGB,
				GL_UNSIGNED_BYTE,
				pixels.data()
			);
			std::ofstream output(_screenshot_path);
			output.write(pixels.data(), pixels.size());
			Quit();
		}
		glutSwapBuffers();
		_clock.Advance(_example->FrameTime());
	}
コード例 #11
0
ファイル: egl_main.cpp プロジェクト: James-Z/oglplus
void make_screenshot(
	eglplus::Surface& surface,
	std::unique_ptr<Example>& example,
	ExampleClock& clock,
	GLuint width,
	GLuint height,
	const char* screenshot_path
)
{
	double s = example->HeatUpTime();
	double t = example->ScreenshotTime();
	double dt = example->FrameTime();

	clock.Update(s);

	// heat-up
	while(true)
	{
		s += dt;
		clock.Update(s);
		example->Render(clock);
		if(s < t) surface.SwapBuffers();
		else break;
	}
	glFinish();
	//save it to a file
	std::vector<char> pixels(width * height * 3);
	glReadPixels(
		0, 0,
		width,
		height,
		GL_RGB,
		GL_UNSIGNED_BYTE,
		pixels.data()
	);
	std::ofstream output(screenshot_path);
	output.write(pixels.data(), pixels.size());
	surface.SwapBuffers();
}
コード例 #12
0
    void Render(ExampleClock& clock) {
        gl.Clear().ColorBuffer().DepthBuffer();

        double time = clock.Now().Seconds();

        auto langle = FullCircles(time / 23.0);
        light_position.Set(
          GLfloat(Cos(langle) * 20.0),
          GLfloat((1.2 + Sin(langle)) * 15.0),
          GLfloat(Sin(langle) * 20.0));

        double x = SineWave(time / 13.0);
        if(x + 0.93 < 0.0)
            clock.Pace(0.2);
        else
            clock.Pace(1.0);

        auto camera = CamMatrixf::Orbiting(
          Vec3f(),
          GLfloat(9.5 + x * 5.1),
          FullCircles(time / 17.0),
          Degrees(SineWave(time / 20.0) * 89));

        camera_matrix.Set(camera);
        camera_position.Set(camera.Position());

        model_matrix.Set(
          ModelMatrixf::TranslationX(+2.0f) *
          ModelMatrixf::RotationX(FullCircles(time / 13.0)));
        shape.Draw();

        model_matrix.Set(
          ModelMatrixf::TranslationX(-2.0f) *
          ModelMatrixf::RotationZ(FullCircles(time / 11.0)));
        shape.Draw();
    }
コード例 #13
0
ファイル: glfw3_main.cpp プロジェクト: JanChou/oglplus
void run_loop(
	std::unique_ptr<Example>& example,
	GLFWwindow* window,
	GLuint width,
	GLuint height
)
{
	GLuint mouse_x = width / 2;
	GLuint mouse_y = height / 2;
	os::steady_clock os_clock;
	ExampleClock clock;
	while(true)
	{
		clock.Update(os_clock.seconds());
		if(!example->Continue(clock)) break;
		example->Render(clock);

		glfwSwapBuffers(window);
		glfwPollEvents();

		int new_x, new_y;
		double tmp_x, tmp_y;
		glfwGetWindowSize(window, &new_x, &new_y);
		if((int(width) != new_x) || (int(height) != new_y))
		{
			if(new_x <= 0) new_x = 1;
			if(new_y <= 0) new_y = 1;
			width = GLuint(new_x);
			height = GLuint(new_y);
			example->Reshape(width, height);
		}

		glfwGetCursorPos(window, &tmp_x, &tmp_y);
		new_x = std::floor(tmp_x);
		new_y = std::floor(tmp_y);
		if((int(mouse_x) != new_x) || (int(mouse_y) != new_y))
		{
			if(new_x <= 0) new_x = 1;
			if(new_y <= 0) new_y = 1;
			mouse_x = GLuint(new_x);
			mouse_y = GLuint(new_y);
			example->MouseMove(
				mouse_x,
				height-
				mouse_y,
				width,
				height
			);
		}

		if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		{
			glfwSetWindowShouldClose(window, 1);
			break;
		}
		if(glfwWindowShouldClose(window))
		{
			break;
		}
	}
}
コード例 #14
0
ファイル: glx_main.cpp プロジェクト: detunized/oglplus
void run_framedump_loop(
	const x11::Display& display,
	const x11::Window& win,
	const glx::Context& ctx,
	std::unique_ptr<Example>& example,
	ExampleClock& clock,
	GLuint width,
	GLuint height,
	const char* framedump_prefix
)
{
	std::vector<char> txtbuf(1024);
	std::cin.getline(txtbuf.data(), txtbuf.size());
	if(std::strcmp(framedump_prefix, txtbuf.data()) != 0) return;

	const std::size_t mouse_path_pts = 7;
	std::vector<Vec2f> mouse_path_pos(mouse_path_pts);
	std::vector<Vec2f> mouse_path_dir(mouse_path_pts);

	for(std::size_t p=0; p!= mouse_path_pts; ++p)
	{
		mouse_path_pos[p] = Vec2f(
			std::rand() % width,
			std::rand() % height
		);
		mouse_path_dir[p] = Vec2f(
			(std::rand()%2?1.0:-1.0)*10.0f*
			(0.2+float(std::rand())/float(RAND_MAX)*0.8),
			(std::rand()%2?1.0:-1.0)*10.0f*
			(0.2+float(std::rand())/float(RAND_MAX)*0.8)
		);
	}

	typedef CubicBezierLoop<Vec2f, double> Loop;

	double t = 0.0;
	double period = 1.0 / 25.0;
	GLuint frame_no = 0;
	std::vector<char> pixels(width * height * 4);

	GLuint border = 32;

	XEvent event;

	while(true)
	{
		while(display.NextEvent(event));

		Vec2f mouse_pos = Loop(mouse_path_pos).Position(t*0.2);

		for(std::size_t p=0; p!= mouse_path_pts; ++p)
		{
			Vec2f dir = mouse_path_dir[p];
			Vec2f pos = mouse_path_pos[p];

			if((pos.x() < border) && (dir.x() < 0.0))
				dir = Vec2f(-dir.x(), dir.y());
			if((pos.y() < border) && (dir.y() < 0.0))
				dir = Vec2f( dir.x(),-dir.y());
			if((pos.x() > width-border) && (dir.x() > 0.0))
				dir = Vec2f(-dir.x(), dir.y());
			if((pos.y() >height-border) && (dir.y() > 0.0))
				dir = Vec2f( dir.x(),-dir.y());

			mouse_path_dir[p] = dir;
			mouse_path_pos[p] = pos + dir;
		}

		float mouse_x = mouse_pos.x();
		float mouse_y = mouse_pos.y();

		if(mouse_x < 0.0f) mouse_x = 0.0f;
		if(mouse_y < 0.0f) mouse_y = 0.0f;
		if(mouse_x > width) mouse_x = width;
		if(mouse_y > height) mouse_y = height;

		example->MouseMove(
			GLuint(mouse_x),
			GLuint(mouse_y),
			width,
			height
		);

		t += period;
		clock.Update(t);
		if(!example->Continue(clock)) break;
		example->Render(clock);
		glFinish();
		glReadPixels(
			0, 0,
			width,
			height,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
			pixels.data()
		);
		glFinish();
		ctx.SwapBuffers(win);
		std::stringstream filename;
		filename <<
			framedump_prefix <<
			std::setfill('0') << std::setw(6) <<
			frame_no << ".rgba";
		{
			std::ofstream file(filename.str());
			file.write(pixels.data(), pixels.size());
			file.flush();
		}
		std::cout << filename.str() << std::endl;
		++frame_no;

		txtbuf.resize(filename.str().size()+1);
		std::cin.getline(txtbuf.data(), txtbuf.size());

		if(std::strncmp(
			filename.str().c_str(),
			txtbuf.data(),
			txtbuf.size()
		) != 0) break;
	}
	while(display.NextEvent(event));
}
コード例 #15
0
ファイル: glx_main.cpp プロジェクト: detunized/oglplus
void run_example_loop(
	const x11::Display& display,
	const x11::Window& win,
	const glx::Context& ctx,
	std::unique_ptr<Example>& example,
	ExampleThreadData::Common& common,
	ExampleClock& clock,
	GLuint width,
	GLuint height
)
{
	std::cout << "-+-[Begin]" << std::endl;
#if GL_ARB_debug_output && !OGLPLUS_NO_LAMBDAS
	ARB_debug_output dbg;
	ARB_debug_output::LogSink sink(
		[](const ARB_debug_output::CallbackData& data) -> void
		{
			std::cout << " |" << std::endl;
			std::cout << " +-+-[" << data.id << "] '" <<
				data.message << "'" << std::endl;
			std::cout << " | +---[source]   '" <<
				EnumValueName(data.source).c_str()  << "'" << std::endl;
			std::cout << " | +---[type]     '" <<
				EnumValueName(data.type).c_str()  << "'" << std::endl;
			std::cout << " | `---[severity] '" <<
				EnumValueName(data.severity).c_str()  << "'" << std::endl;
		}
	);

	dbg.Control(
		DebugOutputARBSource::DontCare,
		DebugOutputARBType::DontCare,
		DebugOutputARBSeverity::Low,
		true
	);

	dbg.InsertMessage(
		DebugOutputARBSource::Application,
		DebugOutputARBType::Other,
		0,
		DebugOutputARBSeverity::Low,
		"Starting main loop"
	);
#endif // GL_ARB_debug_output

	win.SelectInput(
		StructureNotifyMask|
		PointerMotionMask|
		KeyPressMask
	);
	::Atom wmDelete = ::XInternAtom(display, "WM_DELETE_WINDOW", True);
	::XSetWMProtocols(display, win, &wmDelete, 1);

	XEvent event;
	os::steady_clock os_clock;
	bool done = false;
	while(!done && !common.failure)
	{
		while(display.NextEvent(event))
		{
			switch(event.type)
			{
				case ClientMessage:
				case DestroyNotify:
					done = true;
					break;
				case ConfigureNotify:
					width = event.xconfigure.width;
					height = event.xconfigure.height;
					example->Reshape(
						width,
						height
					);
				case MotionNotify:
					example->MouseMove(
						event.xmotion.x,
						height-
						event.xmotion.y,
						width,
						height
					);
					break;
				case KeyPress:
					if(::XLookupKeysym(
						&event.xkey,
						0
					) == XK_Escape) done = true;
					break;
				default:;
			}
		}
		clock.Update(os_clock.seconds());
		if(!example->Continue(clock)) break;
		example->Render(clock);
		ctx.SwapBuffers(win);
	}
	std::cout << " `-[Done]" << std::endl;
}
コード例 #16
0
ファイル: example.hpp プロジェクト: BrainlessLabsInc/oglplus
	/// Rendering procedure with advanced timing
	virtual void Render(ExampleClock& clock)
	{
		this->Render(clock.Now().Seconds());
	}
コード例 #17
0
ファイル: example.hpp プロジェクト: BrainlessLabsInc/oglplus
	/** Implementations of the main function may choose to ignore
	 *  the result of this function or not call it at all.
	 *  This is the overload for advanced timing.
	 */
	virtual bool Continue(const ExampleClock& clock)
	{
		return this->Continue(clock.Now().Seconds());
	}
コード例 #18
0
	bool Continue(const ExampleClock& clock)
	{
		return clock.RealTime().Minutes() < 2.0;
	}
コード例 #19
0
ファイル: egl_main.cpp プロジェクト: Just-D/oglplus
void run_framedump_loop(
	eglplus::Surface& surface,
	std::unique_ptr<Example>& example,
	ExampleClock& clock,
	const ExampleOptions& opts
)
{
	std::vector<char> txtbuf(1024);
	std::cin.getline(txtbuf.data(), txtbuf.size());
	if(std::strcmp(opts.framedump_prefix, txtbuf.data()) != 0) return;

	const std::size_t mouse_path_pts = 7;
	std::vector<Vec2f> mouse_path_pos(mouse_path_pts);
	std::vector<Vec2f> mouse_path_dir(mouse_path_pts);

	for(std::size_t p=0; p!= mouse_path_pts; ++p)
	{
		mouse_path_pos[p] = Vec2f(
			std::rand() % opts.width,
			std::rand() % opts.height
		);
		mouse_path_dir[p] = Vec2f(
			(std::rand()%2?1.0:-1.0)*10.0f*
			(0.2+float(std::rand())/float(RAND_MAX)*0.8),
			(std::rand()%2?1.0:-1.0)*10.0f*
			(0.2+float(std::rand())/float(RAND_MAX)*0.8)
		);
	}

	typedef CubicBezierLoop<Vec2f, double> Loop;

	double t = 0.0;
	double period = 1.0 / 25.0;
	GLuint frame_no = 0;
	std::vector<char> pixels(opts.width * opts.height * 4);

	GLuint border = 32;

	glEnable(GL_MULTISAMPLE);

	while(true)
	{
		Vec2f mouse_pos = Loop(mouse_path_pos).Position(t*0.2);

		for(std::size_t p=0; p!= mouse_path_pts; ++p)
		{
			Vec2f dir = mouse_path_dir[p];
			Vec2f pos = mouse_path_pos[p];

			if((pos.x() < border) && (dir.x() < 0.0))
			{
				dir = Vec2f(-dir.x(), dir.y());
			}
			if((pos.y() < border) && (dir.y() < 0.0))
			{
				dir = Vec2f( dir.x(),-dir.y());
			}
			if((pos.x() > opts.width-border) && (dir.x() > 0.0))
			{
				dir = Vec2f(-dir.x(), dir.y());
			}
			if((pos.y() >opts.height-border) && (dir.y() > 0.0))
			{
				dir = Vec2f( dir.x(),-dir.y());
			}

			mouse_path_dir[p] = dir;
			mouse_path_pos[p] = pos + dir;
		}

		float mouse_x = mouse_pos.x();
		float mouse_y = mouse_pos.y();

		if(mouse_x < 0.0f) mouse_x = 0.0f;
		if(mouse_y < 0.0f) mouse_y = 0.0f;
		if(mouse_x > opts.width) mouse_x = opts.width;
		if(mouse_y > opts.height) mouse_y = opts.height;

		example->MouseMove(
			GLuint(mouse_x),
			GLuint(mouse_y),
			opts.width,
			opts.height
		);

		t += period;
		clock.Update(t);

		if(!example->Continue(clock)) break;

		unsigned part_no = 0;
		double comp = 0.0;
		do
		{
			comp = example->RenderPart(part_no++, clock);
		}
		while(comp < 1.0);

		glFinish();
		glReadPixels(
			0, 0,
			opts.width,
			opts.height,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
			pixels.data()
		);
		glFinish();
		surface.SwapBuffers();
		std::stringstream filename;
		filename <<
			opts.framedump_prefix <<
			std::setfill('0') << std::setw(6) <<
			frame_no << ".rgba";
		{
			std::ofstream file(filename.str());
			file.write(pixels.data(), pixels.size());
			file.flush();
		}
		std::cout << filename.str() << std::endl;
		++frame_no;

		txtbuf.resize(filename.str().size()+1);
		std::cin.getline(txtbuf.data(), txtbuf.size());

		if(std::strncmp(
			filename.str().c_str(),
			txtbuf.data(),
			txtbuf.size()
		) != 0) break;
	}
}