Quake II RTX doxygen  1.0 dev
raster.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 // r_rast.c
19 
20 #include "sw.h"
21 
22 // !!! if these are changed, they must be changed in asm_draw.h too !!!
23 #if (defined __amd64__) || (defined _M_AMD64)
24 #define FULLY_CLIPPED_CACHED 0x8000000000000000ULL
25 #define CLIPPED_NOT_CACHED 0x7FFFFFFFFFFFFFFFULL
26 #define FRAMECOUNT_MASK 0x7FFFFFFFFFFFFFFFULL
27 #else
28 #define FULLY_CLIPPED_CACHED 0x80000000UL
29 #define CLIPPED_NOT_CACHED 0x7FFFFFFFUL
30 #define FRAMECOUNT_MASK 0x7FFFFFFFUL
31 #endif
32 
33 static uintptr_t cacheoffset;
34 
35 int c_faceclip; // number of faces clipped
36 
38 
39 static medge_t *r_pedge;
40 
41 static qboolean r_leftclipped, r_rightclipped;
42 static qboolean r_nearzionly;
43 
44 static mvertex_t r_leftenter, r_leftexit;
45 static mvertex_t r_rightenter, r_rightexit;
46 
47 static int r_emitted;
48 static float r_nearzi;
49 static float r_u1, r_v1, r_lzi1;
50 static int r_ceilv1;
51 
52 static qboolean r_lastvertvalid;
53 
54 
55 /*
56 ================
57 R_EmitEdge
58 ================
59 */
60 static void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1)
61 {
62  edge_t *edge, *pcheck;
63  int u_check;
64  float u, u_step;
65  vec3_t local, transformed;
66  float *world;
67  int v, v2, ceilv0;
68  float scale, lzi0, u0, v0;
69  int side;
70 
71  if (r_lastvertvalid) {
72  u0 = r_u1;
73  v0 = r_v1;
74  lzi0 = r_lzi1;
75  ceilv0 = r_ceilv1;
76  } else {
77  world = &pv0->point[0];
78 
79  // transform and project
80  VectorSubtract(world, modelorg, local);
81  R_TransformVector(local, transformed);
82 
83  if (transformed[2] < NEAR_CLIP)
84  transformed[2] = NEAR_CLIP;
85 
86  lzi0 = 1.0 / transformed[2];
87 
88  // FIXME: build x/yscale into transform?
89  scale = r_refdef.xscale * lzi0;
90  u0 = (r_refdef.xcenter + scale * transformed[0]);
91  if (u0 < r_refdef.fvrectx_adj)
92  u0 = r_refdef.fvrectx_adj;
93  if (u0 > r_refdef.fvrectright_adj)
95 
96  scale = r_refdef.yscale * lzi0;
97  v0 = (r_refdef.ycenter - scale * transformed[1]);
98  if (v0 < r_refdef.fvrecty_adj)
99  v0 = r_refdef.fvrecty_adj;
100  if (v0 > r_refdef.fvrectbottom_adj)
102 
103  ceilv0 = (int) ceil(v0);
104  }
105 
106  world = &pv1->point[0];
107 
108 // transform and project
109  VectorSubtract(world, modelorg, local);
110  R_TransformVector(local, transformed);
111 
112  if (transformed[2] < NEAR_CLIP)
113  transformed[2] = NEAR_CLIP;
114 
115  r_lzi1 = 1.0 / transformed[2];
116 
117  scale = r_refdef.xscale * r_lzi1;
118  r_u1 = (r_refdef.xcenter + scale * transformed[0]);
119  if (r_u1 < r_refdef.fvrectx_adj)
123 
124  scale = r_refdef.yscale * r_lzi1;
125  r_v1 = (r_refdef.ycenter - scale * transformed[1]);
126  if (r_v1 < r_refdef.fvrecty_adj)
130 
131  if (r_lzi1 > lzi0)
132  lzi0 = r_lzi1;
133 
134  if (lzi0 > r_nearzi) // for mipmap finding
135  r_nearzi = lzi0;
136 
137 // for right edges, all we want is the effect on 1/z
138  if (r_nearzionly)
139  return;
140 
141  r_emitted = 1;
142 
143  r_ceilv1 = (int) ceil(r_v1);
144 
145 
146 // create the edge
147  if (ceilv0 == r_ceilv1) {
148  // we cache unclipped horizontal edges as fully clipped
152  }
153 
154  return; // horizontal edge
155  }
156 
157  side = ceilv0 > r_ceilv1;
158 
159  edge = edge_p++;
160 
161  edge->owner = r_pedge;
162 
163  edge->nearzi = lzi0;
164 
165  if (side == 0) {
166  // trailing edge (go from p1 to p2)
167  v = ceilv0;
168  v2 = r_ceilv1 - 1;
169 
170  edge->surfs[0] = surface_p - surfaces;
171  edge->surfs[1] = 0;
172 
173  u_step = ((r_u1 - u0) / (r_v1 - v0));
174  u = u0 + ((float)v - v0) * u_step;
175  } else {
176  // leading edge (go from p2 to p1)
177  v2 = ceilv0 - 1;
178  v = r_ceilv1;
179 
180  edge->surfs[0] = 0;
181  edge->surfs[1] = surface_p - surfaces;
182 
183  u_step = ((u0 - r_u1) / (v0 - r_v1));
184  u = r_u1 + ((float)v - r_v1) * u_step;
185  }
186 
187  edge->u_step = u_step * 0x100000;
188  edge->u = u * 0x100000 + 0xFFFFF;
189 
190 // we need to do this to avoid stepping off the edges if a very nearly
191 // horizontal edge is less than epsilon above a scan, and numeric error causes
192 // it to incorrectly extend to the scan, and the extension of the line goes off
193 // the edge of the screen
194 // FIXME: is this actually needed?
195  if (edge->u < r_refdef.vrect_x_adj_shift20)
197  if (edge->u > r_refdef.vrectright_adj_shift20)
199 
200 //
201 // sort the edge in normally
202 //
203  u_check = edge->u;
204  if (edge->surfs[0])
205  u_check++; // sort trailers after leaders
206 
207  if (!newedges[v] || newedges[v]->u >= u_check) {
208  edge->next = newedges[v];
209  newedges[v] = edge;
210  } else {
211  pcheck = newedges[v];
212  while (pcheck->next && pcheck->next->u < u_check)
213  pcheck = pcheck->next;
214  edge->next = pcheck->next;
215  pcheck->next = edge;
216  }
217 
218  edge->nextremove = removeedges[v2];
219  removeedges[v2] = edge;
220 }
221 
222 
223 /*
224 ================
225 R_ClipEdge
226 ================
227 */
228 static void R_ClipEdge(mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
229 {
230  float d0, d1, f;
231  mvertex_t clipvert;
232 
233  for (; clip; clip = clip->next) {
234  d0 = DotProduct(pv0->point, clip->normal) - clip->dist;
235  d1 = DotProduct(pv1->point, clip->normal) - clip->dist;
236 
237  if (d0 >= 0) {
238  // point 0 is unclipped
239  if (d1 >= 0) {
240  // both points are unclipped
241  continue;
242  }
243 
244  // only point 1 is clipped
245 
246  // we don't cache clipped edges
248 
249  f = d0 / (d0 - d1);
250  LerpVector(pv0->point, pv1->point, f, clipvert.point);
251 
252  if (clip->leftedge) {
253  r_leftclipped = qtrue;
254  r_leftexit = clipvert;
255  } else if (clip->rightedge) {
256  r_rightclipped = qtrue;
257  r_rightexit = clipvert;
258  }
259 
260  R_ClipEdge(pv0, &clipvert, clip->next);
261  return;
262  } else {
263  // point 0 is clipped
264  if (d1 < 0) {
265  // both points are clipped
266  // we do cache fully clipped edges
267  if (!r_leftclipped)
270  return;
271  }
272 
273  // only point 0 is clipped
274  r_lastvertvalid = qfalse;
275 
276  // we don't cache partially clipped edges
278 
279  f = d0 / (d0 - d1);
280  LerpVector(pv0->point, pv1->point, f, clipvert.point);
281 
282  if (clip->leftedge) {
283  r_leftclipped = qtrue;
284  r_leftenter = clipvert;
285  } else if (clip->rightedge) {
286  r_rightclipped = qtrue;
287  r_rightenter = clipvert;
288  }
289 
290  R_ClipEdge(&clipvert, pv1, clip->next);
291  return;
292  }
293  }
294 
295 // add the edge
296  R_EmitEdge(pv0, pv1);
297 }
298 
299 
300 /*
301 ================
302 R_EmitCachedEdge
303 ================
304 */
305 static void R_EmitCachedEdge(void)
306 {
307  edge_t *pedge_t;
308 
309  pedge_t = (edge_t *)((byte *)r_edges + r_pedge->cachededgeoffset);
310 
311  if (!pedge_t->surfs[0])
312  pedge_t->surfs[0] = surface_p - surfaces;
313  else
314  pedge_t->surfs[1] = surface_p - surfaces;
315 
316  if (pedge_t->nearzi > r_nearzi) // for mipmap finding
317  r_nearzi = pedge_t->nearzi;
318 
319  r_emitted = 1;
320 }
321 
322 
323 /*
324 ================
325 R_RenderFace
326 ================
327 */
328 void R_RenderFace(mface_t *fa, int clipflags)
329 {
330  int i;
331  unsigned mask;
332  cplane_t *pplane;
333  float distinv;
334  vec3_t p_normal;
335  medge_t tedge;
336  msurfedge_t *surfedge;
337  clipplane_t *pclip;
338  qboolean makeleftedge, makerightedge;
339 
340  // translucent surfaces are not drawn by the edge renderer
341  if (fa->texinfo->c.flags & SURF_TRANS_MASK) {
342  fa->next = r_alpha_surfaces;
343  r_alpha_surfaces = fa;
344  return;
345  }
346 
347  // sky surfaces encountered in the world will cause the
348  // environment box surfaces to be emited
349  if (fa->texinfo->c.flags & SURF_SKY) {
350  R_EmitSkyBox();
351  return;
352  }
353 
354 // skip out if no more surfs
355  if ((surface_p) >= surf_max) {
356  r_outofsurfaces++;
357  return;
358  }
359 
360 // ditto if not enough edges left, or switch to auxedges if possible
361  if ((edge_p + fa->numsurfedges + 4) >= edge_max) {
362  r_outofedges += fa->numsurfedges;
363  return;
364  }
365 
366  c_faceclip++;
367 
368 // set up clip planes
369  pclip = NULL;
370 
371  for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) {
372  if (clipflags & mask) {
373  view_clipplanes[i].next = pclip;
374  pclip = &view_clipplanes[i];
375  }
376  }
377 
378 // push the edges through
379  r_emitted = 0;
380  r_nearzi = 0;
381  r_nearzionly = qfalse;
382  makeleftedge = makerightedge = qfalse;
383  r_lastvertvalid = qfalse;
384 
385  surfedge = fa->firstsurfedge;
386  for (i = 0; i < fa->numsurfedges; i++, surfedge++) {
387  r_pedge = surfedge->edge;
388 
389  // if the edge is cached, we can just reuse the edge
390  if (!insubmodel) {
391  if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) {
392  if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
393  r_framecount) {
394  r_lastvertvalid = qfalse;
395  continue;
396  }
397  } else {
398  if ((((byte *)edge_p - (byte *)r_edges) >
399  r_pedge->cachededgeoffset) &&
400  (((edge_t *)((byte *)r_edges +
401  r_pedge->cachededgeoffset))->owner == r_pedge)) {
403  r_lastvertvalid = qfalse;
404  continue;
405  }
406  }
407  }
408 
409  // assume it's cacheable
410  cacheoffset = (byte *)edge_p - (byte *)r_edges;
411  r_leftclipped = r_rightclipped = qfalse;
412  R_ClipEdge(r_pedge->v[surfedge->vert ],
413  r_pedge->v[surfedge->vert ^ 1],
414  pclip);
415  r_pedge->cachededgeoffset = cacheoffset;
416 
417  if (r_leftclipped)
418  makeleftedge = qtrue;
419  if (r_rightclipped)
420  makerightedge = qtrue;
421  r_lastvertvalid = qtrue;
422  }
423 
424 // if there was a clip off the left edge, add that edge too
425 // FIXME: faster to do in screen space?
426 // FIXME: share clipped edges?
427  if (makeleftedge) {
428  r_pedge = &tedge;
429  r_lastvertvalid = qfalse;
431  }
432 
433 // if there was a clip off the right edge, get the right r_nearzi
434  if (makerightedge) {
435  r_pedge = &tedge;
436  r_lastvertvalid = qfalse;
437  r_nearzionly = qtrue;
439  }
440 
441 // if no edges made it out, return without posting the surface
442  if (!r_emitted)
443  return;
444 
445  r_polycount++;
446 
447  surface_p->msurf = fa;
449  surface_p->flags = fa->drawflags;
451  surface_p->spanstate = 0;
454  surface_p->spans = NULL;
455 
456  pplane = fa->plane;
457 // FIXME: cache this?
458  R_TransformVector(pplane->normal, p_normal);
459 // FIXME: cache this?
460  distinv = 1.0 / (pplane->dist - DotProduct(modelorg, pplane->normal));
461 
462  surface_p->d_zistepu = p_normal[0] * r_refdef.xscaleinv * distinv;
463  surface_p->d_zistepv = -p_normal[1] * r_refdef.yscaleinv * distinv;
464  surface_p->d_ziorigin = p_normal[2] * distinv -
467 
468  surface_p++;
469 }
470 
471 
472 /*
473 ================
474 R_RenderBmodelFace
475 ================
476 */
477 void R_RenderBmodelFace(bedge_t *pedges, mface_t *psurf)
478 {
479  int i;
480  unsigned mask;
481  cplane_t *pplane;
482  float distinv;
483  vec3_t p_normal;
484  medge_t tedge;
485  clipplane_t *pclip;
486  qboolean makeleftedge, makerightedge;
487 
488  if (psurf->texinfo->c.flags & SURF_TRANS_MASK) {
489  psurf->next = r_alpha_surfaces;
490  r_alpha_surfaces = psurf;
491  return;
492  }
493 
494 // skip out if no more surfs
495  if (surface_p >= surf_max) {
496  r_outofsurfaces++;
497  return;
498  }
499 
500 // ditto if not enough edges left, or switch to auxedges if possible
501  if ((edge_p + psurf->numsurfedges + 4) >= edge_max) {
502  r_outofedges += psurf->numsurfedges;
503  return;
504  }
505 
506  c_faceclip++;
507 
508 // this is a dummy to give the caching mechanism someplace to write to
509  r_pedge = &tedge;
510 
511 // set up clip planes
512  pclip = NULL;
513 
514  for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) {
515  if (r_clipflags & mask) {
516  view_clipplanes[i].next = pclip;
517  pclip = &view_clipplanes[i];
518  }
519  }
520 
521 // push the edges through
522  r_emitted = 0;
523  r_nearzi = 0;
524  r_nearzionly = qfalse;
525  makeleftedge = makerightedge = qfalse;
526 // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching
527 // can be used?
528  r_lastvertvalid = qfalse;
529 
530  for (; pedges; pedges = pedges->pnext) {
531  r_leftclipped = r_rightclipped = qfalse;
532  R_ClipEdge(pedges->v[0], pedges->v[1], pclip);
533 
534  if (r_leftclipped)
535  makeleftedge = qtrue;
536  if (r_rightclipped)
537  makerightedge = qtrue;
538  }
539 
540 // if there was a clip off the left edge, add that edge too
541 // FIXME: faster to do in screen space?
542 // FIXME: share clipped edges?
543  if (makeleftedge) {
544  r_pedge = &tedge;
546  }
547 
548 // if there was a clip off the right edge, get the right r_nearzi
549  if (makerightedge) {
550  r_pedge = &tedge;
551  r_nearzionly = qtrue;
553  }
554 
555 // if no edges made it out, return without posting the surface
556  if (!r_emitted)
557  return;
558 
559  r_polycount++;
560 
561  surface_p->msurf = psurf;
563  surface_p->flags = psurf->drawflags;
564  surface_p->insubmodel = qtrue;
565  surface_p->spanstate = 0;
568  surface_p->spans = NULL;
569 
570  pplane = psurf->plane;
571 // FIXME: cache this?
572  R_TransformVector(pplane->normal, p_normal);
573 // FIXME: cache this?
574  distinv = 1.0 / (pplane->dist - DotProduct(modelorg, pplane->normal));
575 
576  surface_p->d_zistepu = p_normal[0] * r_refdef.xscaleinv * distinv;
577  surface_p->d_zistepv = -p_normal[1] * r_refdef.yscaleinv * distinv;
578  surface_p->d_ziorigin = p_normal[2] * distinv -
581 
582  surface_p++;
583 }
584 
oldrefdef_t::xcenter
float xcenter
Definition: sw.h:145
r_nearzionly
static qboolean r_nearzionly
Definition: raster.c:42
oldrefdef_t::fvrectx_adj
float fvrectx_adj
Definition: sw.h:137
r_emitted
static int r_emitted
Definition: raster.c:47
r_leftenter
static mvertex_t r_leftenter
Definition: raster.c:44
r_nearzi
static float r_nearzi
Definition: raster.c:48
cacheoffset
static uintptr_t cacheoffset
Definition: raster.c:33
r_edges
edge_t * r_edges
Definition: edge.c:32
NEAR_CLIP
#define NEAR_CLIP
Definition: sw.h:103
oldrefdef_t::vrectright_adj_shift20
int vrectright_adj_shift20
Definition: sw.h:139
surf_s::flags
int flags
Definition: sw.h:252
r_framecount
int r_framecount
Definition: main.c:64
oldrefdef_t::xscaleinv
float xscaleinv
Definition: sw.h:147
r_refdef
oldrefdef_t r_refdef
Definition: main.c:57
edge_s::surfs
uint16_t surfs[2]
Definition: sw.h:264
oldrefdef_t::fvrectright_adj
float fvrectright_adj
Definition: sw.h:140
r_rightclipped
static qboolean r_rightclipped
Definition: raster.c:41
surf_s::msurf
mface_t * msurf
Definition: sw.h:253
FRAMECOUNT_MASK
#define FRAMECOUNT_MASK
Definition: raster.c:30
R_EmitSkyBox
void R_EmitSkyBox(void)
Definition: sky.c:116
CLIPPED_NOT_CACHED
#define CLIPPED_NOT_CACHED
Definition: raster.c:29
oldrefdef_t::yscale
float yscale
Definition: sw.h:146
r_rightenter
static mvertex_t r_rightenter
Definition: raster.c:45
clipplane_s::normal
vec3_t normal
Definition: sw.h:194
removeedges
edge_t * removeedges[MAXHEIGHT]
Definition: edge.c:42
r_leftexit
static mvertex_t r_leftexit
Definition: raster.c:44
R_RenderFace
void R_RenderFace(mface_t *fa, int clipflags)
Definition: raster.c:328
c_faceclip
int c_faceclip
Definition: raster.c:35
surf_s::spans
struct espan_s * spans
Definition: sw.h:246
edge_p
edge_t * edge_p
Definition: edge.c:32
edge_s::next
struct edge_s * next
Definition: sw.h:263
r_polycount
int r_polycount
Definition: main.c:66
oldrefdef_t::fvrecty_adj
float fvrecty_adj
Definition: sw.h:137
bedge_s::pnext
struct bedge_s * pnext
Definition: sw.h:190
r_v1
static float r_v1
Definition: raster.c:49
r_outofedges
int r_outofedges
Definition: main.c:38
currententity
entity_t * currententity
Definition: bsp.c:26
r_pedge
static medge_t * r_pedge
Definition: raster.c:39
surf_s::entity
entity_t * entity
Definition: sw.h:254
newedges
edge_t * newedges[MAXHEIGHT]
Definition: edge.c:41
clipplane_s::leftedge
byte leftedge
Definition: sw.h:197
edge_s::u_step
fixed16_t u_step
Definition: sw.h:262
clipplane_s::rightedge
byte rightedge
Definition: sw.h:198
oldrefdef_t::fvrectbottom_adj
float fvrectbottom_adj
Definition: sw.h:140
edge_s::owner
medge_t * owner
Definition: sw.h:267
sw.h
surf_s::nearzi
float nearzi
Definition: sw.h:255
edge_s::nearzi
float nearzi
Definition: sw.h:266
clipplane_s::next
struct clipplane_s * next
Definition: sw.h:196
clipplane_s
Definition: sw.h:193
edge_s::u
fixed16_t u
Definition: sw.h:261
r_u1
static float r_u1
Definition: raster.c:49
edge_s::nextremove
struct edge_s * nextremove
Definition: sw.h:265
r_ceilv1
static int r_ceilv1
Definition: raster.c:50
R_EmitEdge
static void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1)
Definition: raster.c:60
bedge_s
Definition: sw.h:188
r_outofsurfaces
int r_outofsurfaces
Definition: main.c:37
oldrefdef_t::xscale
float xscale
Definition: sw.h:146
modelorg
vec3_t modelorg
Definition: bsp.c:27
surf_s::key
int key
Definition: sw.h:247
surf_s::d_zistepv
float d_zistepv
Definition: sw.h:257
surf_s::d_zistepu
float d_zistepu
Definition: sw.h:257
r_currentkey
int r_currentkey
Definition: edge.c:46
surf_s::d_ziorigin
float d_ziorigin
Definition: sw.h:257
r_leftclipped
static qboolean r_leftclipped
Definition: raster.c:41
surfaces
surf_t * surfaces
Definition: edge.c:35
oldrefdef_t::yscaleinv
float yscaleinv
Definition: sw.h:147
R_ClipEdge
static void R_ClipEdge(mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
Definition: raster.c:228
world
#define world
Definition: g_local.h:550
edge_max
edge_t * edge_max
Definition: edge.c:32
bedge_s::v
mvertex_t * v[2]
Definition: sw.h:189
surf_s::insubmodel
qboolean insubmodel
Definition: sw.h:256
r_alpha_surfaces
mface_t * r_alpha_surfaces
Definition: poly.c:41
surf_max
surf_t * surf_max
Definition: edge.c:35
surface_p
surf_t * surface_p
Definition: edge.c:35
r_clipflags
int r_clipflags
Definition: main.c:44
r_currentbkey
int r_currentbkey
Definition: bsp.c:34
oldrefdef_t::vrect_x_adj_shift20
int vrect_x_adj_shift20
Definition: sw.h:138
R_TransformVector
void R_TransformVector(vec3_t in, vec3_t out)
Definition: misc.c:96
clipplane_s::dist
float dist
Definition: sw.h:195
insubmodel
qboolean insubmodel
Definition: bsp.c:25
R_RenderBmodelFace
void R_RenderBmodelFace(bedge_t *pedges, mface_t *psurf)
Definition: raster.c:477
oldrefdef_t::ycenter
float ycenter
Definition: sw.h:145
FULLY_CLIPPED_CACHED
#define FULLY_CLIPPED_CACHED
Definition: raster.c:28
int
CONST PIXELFORMATDESCRIPTOR int
Definition: wgl.c:26
R_EmitCachedEdge
static void R_EmitCachedEdge(void)
Definition: raster.c:305
view_clipplanes
clipplane_t view_clipplanes[4]
Definition: raster.c:37
r_rightexit
static mvertex_t r_rightexit
Definition: raster.c:45
r_lastvertvalid
static qboolean r_lastvertvalid
Definition: raster.c:52
r_lzi1
static float r_lzi1
Definition: raster.c:49
edge_s
Definition: sw.h:260
surf_s::spanstate
int spanstate
Definition: sw.h:249