1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file r_portal.c
12 /// \brief Software renderer portals.
13
14 #include "r_portal.h"
15 #include "r_plane.h"
16 #include "r_main.h"
17 #include "doomstat.h"
18 #include "p_spec.h" // Skybox viewpoints
19 #include "z_zone.h"
20 #include "r_things.h"
21 #include "r_sky.h"
22
23 UINT8 portalrender; /**< When rendering a portal, it establishes the depth of the current BSP traversal. */
24
25 // Linked list for portals.
26 portal_t *portal_base, *portal_cap;
27
28 line_t *portalclipline;
29 sector_t *portalcullsector;
30 INT32 portalclipstart, portalclipend;
31
32 boolean portalline; // is curline a portal seg?
33
Portal_InitList(void)34 void Portal_InitList (void)
35 {
36 portalrender = 0;
37 portal_base = portal_cap = NULL;
38 }
39
40 /** Store the clipping window for a portal in its given range.
41 *
42 * The window is copied from the current window at the time
43 * the function is called, so it is useful for converting one-sided
44 * lines into portals.
45 */
Portal_ClipRange(portal_t * portal)46 void Portal_ClipRange (portal_t* portal)
47 {
48 INT32 start = portal->start;
49 INT32 end = portal->end;
50 INT16 *ceil = portal->ceilingclip;
51 INT16 *floor = portal->floorclip;
52 fixed_t *scale = portal->frontscale;
53
54 INT32 i;
55 for (i = 0; i < end-start; i++)
56 {
57 *ceil = ceilingclip[start+i];
58 ceil++;
59 *floor = floorclip[start+i];
60 floor++;
61 *scale = frontscale[start+i];
62 scale++;
63 }
64 }
65
66 /** Apply the clipping window from a portal.
67 */
Portal_ClipApply(const portal_t * portal)68 void Portal_ClipApply (const portal_t* portal)
69 {
70 INT32 i;
71 INT32 start = portal->start;
72 INT32 end = portal->end;
73 INT16 *ceil = portal->ceilingclip;
74 INT16 *floor = portal->floorclip;
75 fixed_t *scale = portal->frontscale;
76
77 for (i = 0; i < end-start; i++)
78 {
79 ceilingclip[start+i] = *ceil;
80 ceil++;
81 floorclip[start+i] = *floor;
82 floor++;
83 frontscale[start+i] = *scale;
84 scale++;
85 }
86
87 // HACKS FOLLOW
88 for (i = 0; i < start; i++)
89 {
90 floorclip[i] = -1;
91 ceilingclip[i] = (INT16)viewheight;
92 }
93 for (i = end; i < vid.width; i++)
94 {
95 floorclip[i] = -1;
96 ceilingclip[i] = (INT16)viewheight;
97 }
98 }
99
Portal_Add(const INT16 x1,const INT16 x2)100 static portal_t* Portal_Add (const INT16 x1, const INT16 x2)
101 {
102 portal_t *portal = Z_Malloc(sizeof(portal_t), PU_LEVEL, NULL);
103 INT16 *ceilingclipsave = Z_Malloc(sizeof(INT16)*(x2-x1 + 1), PU_LEVEL, NULL);
104 INT16 *floorclipsave = Z_Malloc(sizeof(INT16)*(x2-x1 + 1), PU_LEVEL, NULL);
105 fixed_t *frontscalesave = Z_Malloc(sizeof(fixed_t)*(x2-x1 + 1), PU_LEVEL, NULL);
106
107 // Linked list.
108 if (!portal_base)
109 {
110 portal_base = portal;
111 portal_cap = portal;
112 }
113 else
114 {
115 portal_cap->next = portal;
116 portal_cap = portal;
117 }
118 portal->next = NULL;
119
120 // Store clipping values so they can be restored once the portal is rendered.
121 portal->ceilingclip = ceilingclipsave;
122 portal->floorclip = floorclipsave;
123 portal->frontscale = frontscalesave;
124 portal->start = x1;
125 portal->end = x2;
126
127 // Increase recursion level.
128 portal->pass = portalrender+1;
129
130 return portal;
131 }
132
Portal_Remove(portal_t * portal)133 void Portal_Remove (portal_t* portal)
134 {
135 portal_base = portal->next;
136 Z_Free(portal->ceilingclip);
137 Z_Free(portal->floorclip);
138 Z_Free(portal->frontscale);
139 Z_Free(portal);
140 }
141
142 /** Creates a portal out of two lines and a determined screen range.
143 *
144 * line1 determines the entrance, and line2 the exit.
145 * x1 and x2 determine the screen's column bounds.
146
147 * The view's offset from the entry line center is obtained,
148 * and then rotated&translated to the exit line's center.
149 * When the portal renders, it will create the illusion of
150 * the two lines being seamed together.
151 */
Portal_Add2Lines(const INT32 line1,const INT32 line2,const INT32 x1,const INT32 x2)152 void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2)
153 {
154 portal_t* portal = Portal_Add(x1, x2);
155
156 // Offset the portal view by the linedef centers
157 line_t* start = &lines[line1];
158 line_t* dest = &lines[line2];
159
160 angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
161
162 fixed_t disttopoint;
163 angle_t angtopoint;
164
165 vertex_t dest_c, start_c;
166
167 // looking glass center
168 start_c.x = (start->v1->x + start->v2->x) / 2;
169 start_c.y = (start->v1->y + start->v2->y) / 2;
170
171 // other side center
172 dest_c.x = (dest->v1->x + dest->v2->x) / 2;
173 dest_c.y = (dest->v1->y + dest->v2->y) / 2;
174
175 disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy);
176 angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy);
177 angtopoint += dangle;
178
179 portal->viewx = dest_c.x + FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
180 portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
181 portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight;
182 portal->viewangle = viewangle + dangle;
183
184 portal->clipline = line2;
185
186 Portal_ClipRange(portal);
187
188 portalline = true; // this tells R_StoreWallRange that curline is a portal seg
189 }
190
191 /** Store the clipping window for a portal using a visplane.
192 *
193 * Since visplanes top/bottom windows work in an identical way,
194 * it can just be copied almost directly.
195 */
Portal_ClipVisplane(const visplane_t * plane,portal_t * portal)196 static void Portal_ClipVisplane (const visplane_t* plane, portal_t* portal)
197 {
198 INT16 start = portal->start;
199 INT16 end = portal->end;
200 INT32 i;
201
202 for (i = 0; i < end - start; i++)
203 {
204 // Invalid column.
205 if (plane->top[i + start] == 65535)
206 {
207 portal->ceilingclip[i] = -1;
208 portal->floorclip[i] = -1;
209 continue;
210 }
211 portal->ceilingclip[i] = plane->top[i + start] - 1;
212 portal->floorclip[i] = plane->bottom[i + start] + 1;
213 portal->frontscale[i] = INT32_MAX;
214 }
215 }
216
217 extern INT32 viewwidth;
218
TrimVisplaneBounds(const visplane_t * plane,INT16 * start,INT16 * end)219 static boolean TrimVisplaneBounds (const visplane_t* plane, INT16* start, INT16* end)
220 {
221 *start = plane->minx;
222 *end = plane->maxx + 1;
223
224 // Visplanes have 1-px pads on their sides (extra columns).
225 // Trim them, else it may render out of bounds.
226 if (*end > viewwidth)
227 *end = viewwidth;
228
229 if (!(*start < *end))
230 return true;
231
232
233 /** Trims a visplane's horizontal gap to match its render area.
234 *
235 * Visplanes' minx/maxx may sometimes exceed the area they're
236 * covering. This merely adjusts the boundaries to the next
237 * valid area.
238 */
239
240 while (plane->bottom[*start] == 0 && plane->top[*start] == 65535 && *start < *end)
241 {
242 (*start)++;
243 }
244
245
246 while (plane->bottom[*end - 1] == 0 && plane->top[*start] == 65535 && *end > *start)
247 {
248 (*end)--;
249 }
250
251 return false;
252 }
253
254 /** Creates a skybox portal out of a visplane.
255 *
256 * Applies the necessary offsets and rotation to give
257 * a depth illusion to the skybox.
258 */
Portal_AddSkybox(const visplane_t * plane)259 void Portal_AddSkybox (const visplane_t* plane)
260 {
261 INT16 start, end;
262 mapheader_t *mh;
263 portal_t* portal;
264
265 if (TrimVisplaneBounds(plane, &start, &end))
266 return;
267
268 portal = Portal_Add(start, end);
269
270 Portal_ClipVisplane(plane, portal);
271
272 portal->viewx = skyboxmo[0]->x;
273 portal->viewy = skyboxmo[0]->y;
274 portal->viewz = skyboxmo[0]->z;
275 portal->viewangle = viewangle + skyboxmo[0]->angle;
276
277 mh = mapheaderinfo[gamemap-1];
278
279 // If a relative viewpoint exists, offset the viewpoint.
280 if (skyboxmo[1])
281 {
282 fixed_t x = 0, y = 0;
283 angle_t ang = skyboxmo[0]->angle>>ANGLETOFINESHIFT;
284
285 if (mh->skybox_scalex > 0)
286 x = (viewx - skyboxmo[1]->x) / mh->skybox_scalex;
287 else if (mh->skybox_scalex < 0)
288 x = (viewx - skyboxmo[1]->x) * -mh->skybox_scalex;
289
290 if (mh->skybox_scaley > 0)
291 y = (viewy - skyboxmo[1]->y) / mh->skybox_scaley;
292 else if (mh->skybox_scaley < 0)
293 y = (viewy - skyboxmo[1]->y) * -mh->skybox_scaley;
294
295 // Apply transform to account for the skybox viewport angle.
296 portal->viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang));
297 portal->viewy += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang));
298 }
299
300 if (mh->skybox_scalez > 0)
301 portal->viewz += viewz / mh->skybox_scalez;
302 else if (mh->skybox_scalez < 0)
303 portal->viewz += viewz * -mh->skybox_scalez;
304
305 portal->clipline = -1;
306 }
307
308 /** Creates portals for the currently existing sky visplanes.
309 * The visplanes are also removed and cleared from the list.
310 */
Portal_AddSkyboxPortals(void)311 void Portal_AddSkyboxPortals (void)
312 {
313 visplane_t *pl;
314 INT32 i;
315 UINT16 count = 0;
316
317 for (i = 0; i < MAXVISPLANES; i++, pl++)
318 {
319 for (pl = visplanes[i]; pl; pl = pl->next)
320 {
321 if (pl->picnum == skyflatnum)
322 {
323 Portal_AddSkybox(pl);
324
325 pl->minx = 0;
326 pl->maxx = -1;
327
328 count++;
329 }
330 }
331 }
332
333 CONS_Debug(DBG_RENDER, "Skybox portals: %d\n", count);
334 }
335