summaryrefslogtreecommitdiff
path: root/xform.c
blob: c661688bead4a413a89ce08c7f271532c6994038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <memdraw.h>
#include <geometry.h>
#include "libobj/obj.h"
#include "graphics.h"
#include "internal.h"

/*
 * transforms p from e's reference frame into
 * the world.
 */
Point3
model2world(Entity *e, Point3 p)
{
	return invrframexform3(p, *e);
}

/*
 * transforms p from the world reference frame
 * to c's one (aka Viewing Coordinate System).
 */
Point3
world2vcs(Camera *c, Point3 p)
{
	return rframexform3(p, *c);
}

/*
 * projects p from the VCS to clip space, placing
 * p.[xyz] ∈ (-∞,-w)∪[-w,w]∪(w,∞) where [-w,w]
 * represents the visibility volume.
 *
 * the clipping planes are:
 *
 * 	|   -w   |   w   |
 *	+----------------+
 * 	| left   | right |
 * 	| bottom | top   |
 * 	| far    | near  |
 */
Point3
vcs2clip(Camera *c, Point3 p)
{
	return xform3(p, c->proj);
}

Point3
world2clip(Camera *c, Point3 p)
{
	return vcs2clip(c, world2vcs(c, p));
}

/*
 * performs the perspective division, placing
 * p.[xyz] ∈ [-1,1] and p.w = 1/z
 * (aka Normalized Device Coordinates).
 *
 * p.w is kept as z⁻¹ so we can later do
 * perspective-correct attribute interpolation.
 */
Point3
clip2ndc(Point3 p)
{
	p.w = p.w == 0? 0: 1.0/p.w;
	p.x *= p.w;
	p.y *= p.w;
	p.z *= p.w;
	return p;
}

/*
 * scales p to fit the destination viewport,
 * placing p.x ∈ [0,width], p.y ∈ [0,height],
 * p.z ∈ [0,1] and leaving p.w intact.
 */
Point3
ndc2viewport(Framebuf *fb, Point3 p)
{
	Matrix3 view = {
		Dx(fb->r)/2.0,             0,       0,       Dx(fb->r)/2.0,
		0,            -Dy(fb->r)/2.0,       0,       Dy(fb->r)/2.0,
		0,                         0, 1.0/2.0,             1.0/2.0,
		0,                         0,       0,                   1,
	};
	double w;

	w = p.w;
	p.w = 1;
	p = xform3(p, view);
	p.w = w;
	return p;
}

Point3
viewport2ndc(Framebuf *fb, Point3 p)
{
	p.x = 2*p.x/Dx(fb->r) - 1;
	p.y = 1 - 2*p.y/Dy(fb->r);
	p.z = 2*p.z - 1;
	p.w = 1;
	return p;
}

Point3
ndc2vcs(Camera *c, Point3 p)
{
	Matrix3 invproj;
	Point3 np;

	memmove(invproj, c->proj, 4*4*sizeof(double));
	invm3(invproj);
	np = xform3(p, invproj);
	np.w = np.w == 0? 0: 1.0/np.w;
	np.x *= np.w;
	np.y *= np.w;
	np.z *= np.w;
	np.w = 1;
	return np;
}

Point3
viewport2vcs(Camera *c, Point3 p)
{
	return ndc2vcs(c, viewport2ndc(c->vp->getfb(c->vp), p));
}

Point3
vcs2world(Camera *c, Point3 p)
{
	return invrframexform3(p, *c);
}

Point3
viewport2world(Camera *c, Point3 p)
{
	return vcs2world(c, viewport2vcs(c, p));
}

Point3
world2model(Entity *e, Point3 p)
{
	return rframexform3(p, *e);
}

void
perspective(Matrix3 m, double fovy, double a, double n, double f)
{
	double cotan;

	cotan = 1/tan(fovy/2);
	memset(m, 0, 4*4*sizeof(double));
	m[0][0] =  cotan/a;
	m[1][1] =  cotan;
	m[2][2] =  (f+n)/(f-n);
	m[2][3] =  2*f*n/(f-n);
	m[3][2] = -1;
}

void
orthographic(Matrix3 m, double l, double r, double b, double t, double n, double f)
{
	identity3(m);
	m[0][0] =  2/(r-l);
	m[1][1] =  2/(t-b);
	m[2][2] =  2/(f-n);
	m[0][3] = -(r+l)/(r-l);
	m[1][3] = -(t+b)/(t-b);
	m[2][3] = -(f+n)/(f-n);
}