/*	kd:

	This does projection stuff in Carmack / software renderer. It's conceptually
	simpler, but nonetheless a little tricky to understand if you're
	used to open-gl.

	*/

class dtLe_SwScreen : dtLe_ProjScreen {

	// kd: Less info necessary than for open-gl, but it's there.
	protected vector2				right_planar_unit;
	protected vector3				forw_planar_unit;

	override void Reorient (vector3 world_view_pos, vector3 world_ang) {
		super.Reorient(world_view_pos, world_ang);

		right_planar_unit = (
			sin(view_ang.x),
		-	cos(view_ang.x));

		forw_planar_unit = (
		-	right_planar_unit.y,
			right_planar_unit.x,
			tan(view_ang.y));
	}

	// kd: Projection:
	protected vector3				forw_planar_in;
	protected vector2				right_planar_in;

	override void BeginProjection () {

		// kd: This doesn't cause any imprecisions. It also prevents two
		// multiplications with every projection.
		right_planar_in		= right_planar_unit / tan_fov_2.x;
		forw_planar_in		= forw_planar_unit;
	}

	override void ProjectWorldPos (vector3 world_pos) {

		// kd: Your view is flat. If you pitch up or down, imagine that all the
		// actors move up and down in reality. That's effectively how it works.
		// You can see this in the addition to diff.z.
		diff		= levellocals.vec3diff(view_pos, world_pos);
		depth		= forw_planar_in.xy dot diff.xy;
		diff.z		+= forw_planar_in.z * depth;
		proj_pos	= (
			right_planar_in dot diff.xy,
		-	pixel_stretch * diff.z / tan_fov_2.y);
	}

	override void ProjectActorPos (Actor mo, vector3 offset, double t) {
		let inter_pos = mo.prev + t * (mo.pos - mo.prev);
		ProjectWorldPos(inter_pos + offset);
	}

	override void ProjectActorPosPortal (Actor mo, vector3 offset, double t) {
		let inter_pos = mo.prev + t * levellocals.vec3diff(mo.prev, mo.pos);
		ProjectWorldPos(inter_pos + offset);
	}

	override vector2 ProjectToNormal () const {
		return proj_pos / depth;
	}

	override vector2 ProjectToScreen () const {
		let normal_pos = proj_pos / depth + (1, 1);

		return 0.5 * (
			normal_pos.x * resolution.x,
			normal_pos.y * resolution.y);
	}

	override vector2 ProjectToCustom (
	vector2	origin,
	vector2	resolution) const {
		let normal_pos = proj_pos / depth + (1, 1);

		return origin + 0.5 * (
			normal_pos.x * resolution.x,
			normal_pos.y * resolution.y);
	}

	// kd: Just as simple. You again assume you are trying to reverse a
	// projected position from the screen back into the world.
	protected vector3				forw_planar_out;
	protected vector3				right_planar_out;
	protected vector3				down_planar_out;

	override void BeginDeprojection () {
		forw_planar_out.xy	= forw_planar_unit.xy;
		forw_planar_out.z	= 0;
		right_planar_out.xy	= tan_fov_2.x * right_planar_unit;
		right_planar_out.z	= 0;
		down_planar_out		= (
			0,
			0,
			tan_fov_2.y / pixel_stretch);
	}

	override vector3 DeprojectNormalToDiff (
	vector2	normal_pos,
	double	depth) const {
		return depth * (
			forw_planar_out +
			normal_pos.x * right_planar_out +
		-	(0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
	}

	override vector3 DeprojectScreenToDiff (
	vector2	screen_pos,
	double	depth) const {

		// kd: Same thing...
		let normal_pos = 2 * (
			screen_pos.x / resolution.x,
			screen_pos.y / resolution.y) - (1, 1);

		return depth * (
			forw_planar_out +
			normal_pos.x * right_planar_out +
		-	(0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
	}
}