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
|
// worldocull.cpp: occlusion map and occlusion test
#include "cube.h"
#define NUMRAYS 512
float rdist[NUMRAYS];
bool ocull = true;
float odist = 256;
void toggleocull() { ocull = !ocull; };
COMMAND(toggleocull, ARG_NONE);
// constructs occlusion map: cast rays in all directions on the 2d plane and record distance.
// done exactly once per frame.
void computeraytable(float vx, float vy)
{
if(!ocull) return;
odist = getvar("fog")*1.5f;
float apitch = (float)fabs(player1->pitch);
float af = getvar("fov")/2+apitch/1.5f+3;
float byaw = (player1->yaw-90+af)/360*PI2;
float syaw = (player1->yaw-90-af)/360*PI2;
loopi(NUMRAYS)
{
float angle = i*PI2/NUMRAYS;
if((apitch>45 // must be bigger if fov>120
|| (angle<byaw && angle>syaw)
|| (angle<byaw-PI2 && angle>syaw-PI2)
|| (angle<byaw+PI2 && angle>syaw+PI2))
&& !OUTBORD(vx, vy)
&& !SOLID(S(fast_f2nat(vx), fast_f2nat(vy)))) // try to avoid tracing ray if outside of frustrum
{
float ray = i*8/(float)NUMRAYS;
float dx, dy;
if(ray>1 && ray<3) { dx = -(ray-2); dy = 1; }
else if(ray>=3 && ray<5) { dx = -1; dy = -(ray-4); }
else if(ray>=5 && ray<7) { dx = ray-6; dy = -1; }
else { dx = 1; dy = ray>4 ? ray-8 : ray; };
float sx = vx;
float sy = vy;
for(;;)
{
sx += dx;
sy += dy;
if(SOLID(S(fast_f2nat(sx), fast_f2nat(sy)))) // 90% of time spend in this function is on this line
{
rdist[i] = (float)(fabs(sx-vx)+fabs(sy-vy));
break;
};
};
}
else
{
rdist[i] = 2;
};
};
};
// test occlusion for a cube... one of the most computationally expensive functions in the engine
// as its done for every cube and entity, but its effect is more than worth it!
inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; };
inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; };
int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = viewer, c = cube to test
{
if(!ocull) return 0;
float nx = vx, ny = vy; // n = point on the border of the cube that is closest to v
if(nx<cx) nx = cx;
else if(nx>cx+csize) nx = cx+csize;
if(ny<cy) ny = cy;
else if(ny>cy+csize) ny = cy+csize;
float xdist = (float)fabs(nx-vx);
float ydist = (float)fabs(ny-vy);
if(xdist>odist || ydist>odist) return 2;
float dist = xdist+ydist-1; // 1 needed?
// ABC
// D E
// FGH
// - check middle cube? BG
// find highest and lowest angle in the occlusion map that this cube spans, based on its most left and right
// points on the border from the viewer pov... I see no easier way to do this than this silly code below
float h, l;
if(cx<=vx) // ABDFG
{
if(cx+csize<vx) // ADF
{
if(cy<=vy) // AD
{
if(cy+csize<vy) { h = ca(-(cx-vx), -(cy+csize-vy))+4; l = ca(-(cx+csize-vx), -(cy-vy))+4; } // A
else { h = ma(-(cx+csize-vx), -(cy+csize-vy))+4; l = ma(-(cx+csize-vx), -(cy-vy))+4; } // D
}
else { h = ca(cy+csize-vy, -(cx+csize-vx))+2; l = ca(cy-vy, -(cx-vx))+2; }; // F
}
else // BG
{
if(cy<=vy)
{
if(cy+csize<vy) { h = ma(-(cy+csize-vy), cx-vx)+6; l = ma(-(cy+csize-vy), cx+csize-vx)+6; } // B
else return 0;
}
else { h = ma(cy-vy, -(cx+csize-vx))+2; l = ma(cy-vy, -(cx-vx))+2; }; // G
};
}
else // CEH
{
if(cy<=vy) // CE
{
if(cy+csize<vy) { h = ca(-(cy-vy), cx-vx)+6; l = ca(-(cy+csize-vy), cx+csize-vx)+6; } // C
else { h = ma(cx-vx, cy-vy); l = ma(cx-vx, cy+csize-vy); }; // E
}
else { h = ca(cx+csize-vx, cy-vy); l = ca(cx-vx, cy+csize-vy); }; // H
};
int si = fast_f2nat(h*(NUMRAYS/8))+NUMRAYS; // get indexes into occlusion map from angles
int ei = fast_f2nat(l*(NUMRAYS/8))+NUMRAYS+1;
if(ei<=si) ei += NUMRAYS;
for(int i = si; i<=ei; i++)
{
if(dist<rdist[i&(NUMRAYS-1)]) return 0; // if any value in this segment of the occlusion map is further away then cube is not occluded
};
return 1; // cube is entirely occluded
};
|
>
>
>
|
>
|
|
>
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
>
|
|
|
>
|
>
>
>
|
>
>
|
>
|
>
>
|
|
|
<
|
|
>
>
|
>
|
|
>
|
|
|
<
|
<
|
|
|
|
|
>
>
|
>
>
>
>
|
|
>
>
>
>
|
>
>
|
>
>
|
>
|
>
|
>
|
>
|
|
|
|
>
|
|
|
|
|
|
>
|
>
|
|
|
|
|
|
|
>
|
>
>
>
>
>
|
>
>
>
>
|
>
>
|
|
|
|
|
<
>
>
|
>
>
>
>
|
|
>
|
|
|
|
|
|
|
>
|
>
>
>
|
>
|
>
|
>
>
|
>
|
|
>
|
|
<
>
>
|
>
|
|
<
|
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
// worldocull.cpp: occlusion map and occlusion test
#include "cube.h"
#define NUMRAYS 512
float rdist[NUMRAYS];
bool ocull = true;
float odist = 256;
void
toggleocull()
{
ocull = !ocull;
};
COMMAND(toggleocull, ARG_NONE);
// constructs occlusion map: cast rays in all directions on the 2d plane and
// record distance. done exactly once per frame.
void
computeraytable(float vx, float vy)
{
if (!ocull)
return;
odist = getvar("fog") * 1.5f;
float apitch = (float)fabs(player1->pitch);
float af = getvar("fov") / 2 + apitch / 1.5f + 3;
float byaw = (player1->yaw - 90 + af) / 360 * PI2;
float syaw = (player1->yaw - 90 - af) / 360 * PI2;
loopi(NUMRAYS)
{
float angle = i * PI2 / NUMRAYS;
if ((apitch > 45 // must be bigger if fov>120
|| (angle < byaw && angle > syaw) ||
(angle < byaw - PI2 && angle > syaw - PI2) ||
(angle < byaw + PI2 && angle > syaw + PI2)) &&
!OUTBORD(vx, vy) &&
!SOLID(S(fast_f2nat(vx),
fast_f2nat(vy)))) // try to avoid tracing ray if outside
// of frustrum
{
float ray = i * 8 / (float)NUMRAYS;
float dx, dy;
if (ray > 1 && ray < 3) {
dx = -(ray - 2);
dy = 1;
} else if (ray >= 3 && ray < 5) {
dx = -1;
dy = -(ray - 4);
} else if (ray >= 5 && ray < 7) {
dx = ray - 6;
dy = -1;
} else {
dx = 1;
dy = ray > 4 ? ray - 8 : ray;
};
float sx = vx;
float sy = vy;
for (;;) {
sx += dx;
sy += dy;
if (SOLID(S(fast_f2nat(sx),
fast_f2nat(
sy)))) // 90% of time spend in this
// function is on this line
{
rdist[i] = (float)(fabs(sx - vx) +
fabs(sy - vy));
break;
};
};
} else {
rdist[i] = 2;
};
};
};
// test occlusion for a cube... one of the most computationally expensive
// functions in the engine as its done for every cube and entity, but its effect
// is more than worth it!
inline float
ca(float x, float y)
{
return x > y ? y / x : 2 - x / y;
};
inline float
ma(float x, float y)
{
return x == 0 ? (y > 0 ? 2 : -2) : y / x;
};
int
isoccluded(float vx, float vy, float cx, float cy,
float csize) // v = viewer, c = cube to test
{
if (!ocull)
return 0;
float
nx = vx,
ny = vy; // n = point on the border of the cube that is closest to v
if (nx < cx)
nx = cx;
else if (nx > cx + csize)
nx = cx + csize;
if (ny < cy)
ny = cy;
else if (ny > cy + csize)
ny = cy + csize;
float xdist = (float)fabs(nx - vx);
float ydist = (float)fabs(ny - vy);
if (xdist > odist || ydist > odist)
return 2;
float dist = xdist + ydist - 1; // 1 needed?
// ABC
// D E
// FGH
// - check middle cube? BG
// find highest and lowest angle in the occlusion map that this cube
// spans, based on its most left and right points on the border from the
// viewer pov... I see no easier way to do this than this silly code
// below
float h, l;
if (cx <= vx) // ABDFG
{
if (cx + csize < vx) // ADF
{
if (cy <= vy) // AD
{
if (cy + csize < vy) {
h = ca(-(cx - vx), -(cy + csize - vy)) +
4;
l = ca(-(cx + csize - vx), -(cy - vy)) +
4;
} // A
else {
h = ma(-(cx + csize - vx),
-(cy + csize - vy)) +
4;
l = ma(-(cx + csize - vx), -(cy - vy)) +
4;
} // D
} else {
h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
l = ca(cy - vy, -(cx - vx)) + 2;
}; // F
} else // BG
{
if (cy <= vy) {
if (cy + csize < vy) {
h = ma(-(cy + csize - vy), cx - vx) + 6;
l = ma(-(cy + csize - vy),
cx + csize - vx) +
6;
} // B
else
return 0;
} else {
h = ma(cy - vy, -(cx + csize - vx)) + 2;
l = ma(cy - vy, -(cx - vx)) + 2;
}; // G
};
} else // CEH
{
if (cy <= vy) // CE
{
if (cy + csize < vy) {
h = ca(-(cy - vy), cx - vx) + 6;
l = ca(-(cy + csize - vy), cx + csize - vx) + 6;
} // C
else {
h = ma(cx - vx, cy - vy);
l = ma(cx - vx, cy + csize - vy);
}; // E
} else {
h = ca(cx + csize - vx, cy - vy);
l = ca(cx - vx, cy + csize - vy);
}; // H
};
int si = fast_f2nat(h * (NUMRAYS / 8)) +
NUMRAYS; // get indexes into occlusion map from angles
int ei = fast_f2nat(l * (NUMRAYS / 8)) + NUMRAYS + 1;
if (ei <= si)
ei += NUMRAYS;
for (int i = si; i <= ei; i++) {
if (dist < rdist[i & (NUMRAYS - 1)])
return 0; // if any value in this segment of the
// occlusion map is further away then cube is
// not occluded
};
return 1; // cube is entirely occluded
};
|