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