<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">[PATCH] Allow up to 65520 clipnodes

The BSP file format's original design limits us to having 32767 clipnodes by
having the offsets stored as 16-bit signed values. Negative values are to
signify leaf nodes and refer to the CONTENTS_xxx flags defined in bspfile.h.

Since only a handful of values are required for the CONTENTS_xxx flags, we can
make use of all the space on the negative side be being a bit more careful
about how we handle these offsets.

Essentially, we can treat the offset as unsigned and instead of checking for a
value &lt; 0 to signify contents, we check that the value large enough to be
contents. This is binary compatible with existing maps.

I added a helper function clipnode_child() to extract a 32-bit signed integer
value from the dclipnode_t structure. If the child index is large enough to be
a contents flag, we return the negative value. Otherwise, we return the index
between 0 and 65520 (0xfff0).

Hopefully my attempt at updating the asm code looks okay - this part of the
patch is the result of a 1-day crash course in x86 asm.

diff -urN a/NQ/world.c head/NQ/world.c
--- a/NQ/world.c	2005-01-29 11:16:40.000000000 +1030
+++ head/NQ/world.c	2006-03-09 17:56:23.000000000 +1030
@@ -482,9 +482,12 @@
     dclipnode_t *node;
     mplane_t *plane;
 
-    while (num &gt;= 0) {
+    if (num &lt; 0)
+	return num;
+
+    while (num &lt; 0xfff0) {
 	if (num &lt; hull-&gt;firstclipnode || num &gt; hull-&gt;lastclipnode)
-	    Sys_Error("%s: bad node number", __func__);
+	    Sys_Error("%s: bad node number (%i)", __func__, num);
 
 	node = hull-&gt;clipnodes + num;
 	plane = hull-&gt;planes + node-&gt;planenum;
@@ -494,12 +497,13 @@
 	else
 	    d = DotProduct(plane-&gt;normal, p) - plane-&gt;dist;
 	if (d &lt; 0)
-	    num = node-&gt;children[1];
+	    num = *(uint16_t *)&amp;node-&gt;children[1];
 	else
-	    num = node-&gt;children[0];
+	    num = *(uint16_t *)&amp;node-&gt;children[0];
     }
 
-    return num;
+    /* Convert back to negative contents */
+    return num - 0x10000;
 }
 
 #endif // !id386
@@ -614,19 +618,23 @@
     }
 
 #if 1
-    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0)
-	return SV_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0)
-	return SV_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0) {
+	i = clipnode_child(node, 0);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0) {
+	i = clipnode_child(node, 1);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #else
-    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0))
-	return SV_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0))
-	return SV_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0)) {
+	i = clipnode_child(node, 0);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0)) {
+	i = clipnode_child(node, 1);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #endif
 
 // put the crosspoint DIST_EPSILON pixels on the near side
@@ -646,23 +654,21 @@
     side = (t1 &lt; 0);
 
 // move up to the node
-    if (!SV_RecursiveHullCheck
-	(hull, node-&gt;children[side], p1f, midf, p1, mid, trace))
+    i = clipnode_child(node, side);
+    if (!SV_RecursiveHullCheck(hull, i, p1f, midf, p1, mid, trace))
 	return false;
 
 #ifdef PARANOID
-    if (SV_HullPointContents(sv_hullmodel, mid, node-&gt;children[side])
-	== CONTENTS_SOLID) {
+    if (SV_HullPointContents(sv_hullmodel, mid, i) == CONTENTS_SOLID) {
 	Con_Printf("mid PointInHullSolid\n");
 	return false;
     }
 #endif
 
-    if (SV_HullPointContents(hull, node-&gt;children[side ^ 1], mid)
-	!= CONTENTS_SOLID)
-// go past the node
-	return SV_RecursiveHullCheck(hull, node-&gt;children[side ^ 1], midf,
-				     p2f, mid, p2, trace);
+    i = clipnode_child(node, side ^ 1);
+    if (SV_HullPointContents(hull, i, mid) != CONTENTS_SOLID)
+	/* go past the node */
+	return SV_RecursiveHullCheck(hull, i, midf, p2f, mid, p2, trace);
 
     if (trace-&gt;allsolid)
 	return false;		// never got out of the solid area
diff -urN a/NQ/worlda.S head/NQ/worlda.S
--- a/NQ/worlda.S	2004-08-04 21:21:25.000000000 +0930
+++ head/NQ/worlda.S	2006-03-09 19:25:51.000000000 +1030
@@ -71,7 +71,7 @@
 // %edi: hull-&gt;clipnodes
 // %ebp: hull-&gt;planes
 
-//	while (num &gt;= 0)
+//	while (num &lt; 0xfff0)
 //	{
 
 Lhloop:
@@ -111,8 +111,9 @@
 	fsubrs	(%edx,%ebx,4)
 
 Lsub:
-	sarl	$16,%eax
-	sarl	$16,%esi
+// Note: treating children as unsigned
+	shrl	$16,%eax
+	shrl	$16,%esi
 
 //		if (d &lt; 0)
 //			num = node-&gt;children[1];
@@ -125,7 +126,13 @@
 	xorl	$0xFFFFFFFF,%ecx
 	andl	%ecx,%eax
 	orl	%esi,%eax
-	jns	Lhloop
+
+	cmpl	$0x0000FFF0,%eax
+	jb	Lhloop
+
+// Re-interpret sign bit and extend
+	shll	$16,%eax
+	sarl	$16,%eax
 
 //	return num;
 Lhdone:
diff -urN a/QW/common/pmovetst.c head/QW/common/pmovetst.c
--- a/QW/common/pmovetst.c	2004-07-25 15:59:58.000000000 +0930
+++ head/QW/common/pmovetst.c	2006-03-09 17:59:40.000000000 +1030
@@ -115,7 +115,10 @@
     dclipnode_t *node;
     mplane_t *plane;
 
-    while (num &gt;= 0) {
+    if (num &lt; 0)
+	return num;
+
+    while (num &lt; 0xfff0) {
 	if (num &lt; hull-&gt;firstclipnode || num &gt; hull-&gt;lastclipnode)
 	    Sys_Error("PM_HullPointContents: bad node number");
 
@@ -127,12 +130,13 @@
 	else
 	    d = DotProduct(plane-&gt;normal, p) - plane-&gt;dist;
 	if (d &lt; 0)
-	    num = node-&gt;children[1];
+	    num = *(uint16_t *)&amp;node-&gt;children[1];
 	else
-	    num = node-&gt;children[0];
+	    num = *(uint16_t *)&amp;node-&gt;children[0];
     }
 
-    return num;
+    /* Convert back to negative contents */
+    return num - 0x10000;
 }
 
 /*
@@ -151,10 +155,12 @@
     int num;
 
     hull = &amp;pmove.physents[0].model-&gt;hulls[0];
-
     num = hull-&gt;firstclipnode;
 
-    while (num &gt;= 0) {
+    if (num &lt; 0)
+	return num;
+
+    while (num &lt; 0xfff0) {
 	if (num &lt; hull-&gt;firstclipnode || num &gt; hull-&gt;lastclipnode)
 	    Sys_Error("PM_HullPointContents: bad node number");
 
@@ -166,12 +172,13 @@
 	else
 	    d = DotProduct(plane-&gt;normal, p) - plane-&gt;dist;
 	if (d &lt; 0)
-	    num = node-&gt;children[1];
+	    num = *(uint16_t *)&amp;node-&gt;children[1];
 	else
-	    num = node-&gt;children[0];
+	    num = *(uint16_t *)&amp;node-&gt;children[0];
     }
 
-    return num;
+    /* Convert back to negative contents */
+    return num - 0x10000;
 }
 
 /*
@@ -235,19 +242,23 @@
     }
 
 #if 1
-    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0)
-	return PM_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0)
-	return PM_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0) {
+	i = clipnode_child(node, 0);
+	return PM_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0) {
+	i = clipnode_child(node, 1);
+	return PM_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #else
-    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0))
-	return PM_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0))
-	return PM_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0)) {
+	i = clipnode_child(node, 0);
+	return PM_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0)) {
+	i = clipnode_child(node, 1);
+	return PM_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #endif
 
 // put the crosspoint DIST_EPSILON pixels on the near side
@@ -267,23 +278,21 @@
     side = (t1 &lt; 0);
 
 // move up to the node
-    if (!PM_RecursiveHullCheck
-	(hull, node-&gt;children[side], p1f, midf, p1, mid, trace))
+    i = clipnode_child(node, side);
+    if (!PM_RecursiveHullCheck(hull, i, p1f, midf, p1, mid, trace))
 	return false;
 
 #ifdef PARANOID
-    if (PM_HullPointContents(pm_hullmodel, mid, node-&gt;children[side])
-	== CONTENTS_SOLID) {
+    if (PM_HullPointContents(pm_hullmodel, i, mid) == CONTENTS_SOLID) {
 	Con_Printf("mid PointInHullSolid\n");
 	return false;
     }
 #endif
 
-    if (PM_HullPointContents(hull, node-&gt;children[side ^ 1], mid)
-	!= CONTENTS_SOLID)
-// go past the node
-	return PM_RecursiveHullCheck(hull, node-&gt;children[side ^ 1], midf,
-				     p2f, mid, p2, trace);
+    i = clipnode_child(node, side ^ 1);
+    if (PM_HullPointContents(hull, i, mid) != CONTENTS_SOLID)
+	/* go past the node */
+	return PM_RecursiveHullCheck(hull, i, midf, p2f, mid, p2, trace);
 
     if (trace-&gt;allsolid)
 	return false;		// never got out of the solid area
diff -urN a/QW/server/world.c head/QW/server/world.c
--- a/QW/server/world.c	2004-08-01 02:34:40.000000000 +0930
+++ head/QW/server/world.c	2006-03-09 17:56:16.000000000 +1030
@@ -467,9 +467,12 @@
     dclipnode_t *node;
     mplane_t *plane;
 
-    while (num &gt;= 0) {
+    if (num &lt; 0)
+	return num;
+
+    while (num &lt; 0xfff0) {
 	if (num &lt; hull-&gt;firstclipnode || num &gt; hull-&gt;lastclipnode)
-	    SV_Error("SV_HullPointContents: bad node number");
+	    SV_Error("%s: bad node number (%i)", __func__, num);
 
 	node = hull-&gt;clipnodes + num;
 	plane = hull-&gt;planes + node-&gt;planenum;
@@ -479,12 +482,13 @@
 	else
 	    d = DotProduct(plane-&gt;normal, p) - plane-&gt;dist;
 	if (d &lt; 0)
-	    num = node-&gt;children[1];
+	    num = *(uint16_t *)&amp;node-&gt;children[1];
 	else
-	    num = node-&gt;children[0];
+	    num = *(uint16_t *)&amp;node-&gt;children[0];
     }
 
-    return num;
+    /* Convert back to negative contents */
+    return num - 0x10000;
 }
 
 #endif // !id386
@@ -588,19 +592,23 @@
     }
 
 #if 1
-    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0)
-	return SV_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0)
-	return SV_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if (t1 &gt;= 0 &amp;&amp; t2 &gt;= 0) {
+	i = clipnode_child(node, 0);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if (t1 &lt; 0 &amp;&amp; t2 &lt; 0) {
+	i = clipnode_child(node, 1);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #else
-    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0))
-	return SV_RecursiveHullCheck(hull, node-&gt;children[0], p1f, p2f, p1,
-				     p2, trace);
-    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0))
-	return SV_RecursiveHullCheck(hull, node-&gt;children[1], p1f, p2f, p1,
-				     p2, trace);
+    if ((t1 &gt;= DIST_EPSILON &amp;&amp; t2 &gt;= DIST_EPSILON) || (t2 &gt; t1 &amp;&amp; t1 &gt;= 0)) {
+	i = clipnode_child(node, 0);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
+    if ((t1 &lt;= -DIST_EPSILON &amp;&amp; t2 &lt;= -DIST_EPSILON) || (t2 &lt; t1 &amp;&amp; t1 &lt;= 0)) {
+	i = clipnode_child(node, 1);
+	return SV_RecursiveHullCheck(hull, i, p1f, p2f, p1, p2, trace);
+    }
 #endif
 
 // put the crosspoint DIST_EPSILON pixels on the near side
@@ -620,23 +628,21 @@
     side = (t1 &lt; 0);
 
 // move up to the node
-    if (!SV_RecursiveHullCheck
-	(hull, node-&gt;children[side], p1f, midf, p1, mid, trace))
+    i = clipnode_child(node, side);
+    if (!SV_RecursiveHullCheck(hull, i, p1f, midf, p1, mid, trace))
 	return false;
 
 #ifdef PARANOID
-    if (SV_HullPointContents(sv_hullmodel, mid, node-&gt;children[side])
-	== CONTENTS_SOLID) {
+    if (SV_HullPointContents(sv_hullmodel, mid, i) == CONTENTS_SOLID) {
 	Con_Printf("mid PointInHullSolid\n");
 	return false;
     }
 #endif
 
-    if (SV_HullPointContents(hull, node-&gt;children[side ^ 1], mid)
-	!= CONTENTS_SOLID)
-// go past the node
-	return SV_RecursiveHullCheck(hull, node-&gt;children[side ^ 1], midf,
-				     p2f, mid, p2, trace);
+    i = clipnode_child(node, side ^ 1);
+    if (SV_HullPointContents(hull, i, mid) != CONTENTS_SOLID)
+	/* go past the node */
+	return SV_RecursiveHullCheck(hull, i, midf, p2f, mid, p2, trace);
 
     if (trace-&gt;allsolid)
 	return false;		// never got out of the solid area
diff -urN a/include/bspfile.h head/include/bspfile.h
--- a/include/bspfile.h	2004-07-25 16:00:01.000000000 +0930
+++ head/include/bspfile.h	2006-03-09 16:50:43.000000000 +1030
@@ -157,11 +157,30 @@
     uint16_t numfaces;	// counting both sides
 } dnode_t;
 
+/*
+ * Note that children are interpreted as unsigned values now, so that we can
+ * handle &gt; 32k clipnodes. Values &gt; 0xFFF0 can be assumed to be CONTENTS
+ * values and can be read as the signed value to be compatible with the above
+ * (i.e. simply subtract 65536).
+ *
+ * I should probably change the type here to uint16_t eventaully and fix up
+ * the rest of the code.
+ */
 typedef struct {
     int32_t planenum;
-    int16_t children[2];		// negative numbers are contents
+    int16_t children[2];
 } dclipnode_t;
 
+static inline int
+clipnode_child(dclipnode_t *node, int child)
+{
+    int ret = *(uint16_t *)&amp;node-&gt;children[child];
+    if (ret &gt; 0xfff0)
+	ret -= 0x10000;
+
+    return ret;
+}
+
 
 typedef struct texinfo_s {
     float vecs[2][4];		// [s/t][xyz offset]
</pre></body></html>