{"id":98,"date":"2025-12-12T10:15:02","date_gmt":"2025-12-12T10:15:02","guid":{"rendered":"https:\/\/3d-galaxy.com\/blog\/?p=98"},"modified":"2025-12-13T05:40:27","modified_gmt":"2025-12-13T05:40:27","slug":"z-order-curves","status":"publish","type":"post","link":"https:\/\/3d-galaxy.com\/blog\/index.php\/2025\/12\/12\/z-order-curves\/","title":{"rendered":"Z-Order Curves"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u261d\ufe0f\ud83e\udd13 First off, Z-order curves have nothing to do with rendering order or Z-depth.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rather, such a &#8220;curve&#8221; is a useful mathematical tool for calculating a value, or code, for some entity, such as a point or a triangle, which approximates the relative position of that entity within some spatial grid. Entities which are closer together in space will have closer values, and entities which are far away from each other will have a greater difference in their calculated value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Z-order curve is named the way that it is because each successive code in the sequence takes a zigzagging, Z-shaped path. Sometimes the Z-order curve value is referred to as a Morton code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Z-order curves provide an effective way to sort points, triangles, or other spatially located items in N-dimensional-space such that they fill space efficiently in an ordered way. For example, all of the triangles which are spatially near each other in 3D space will have a tendency to be near each other along a Z-order curve, and so it provides an effective way of packing points and triangles based on how close they are to each other.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The diagram below demonstrates how a Z-order curve works in 2D.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"943\" src=\"https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Z-curve45.svg_.png\" alt=\"\" class=\"wp-image-89\" srcset=\"https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Z-curve45.svg_.png 960w, https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Z-curve45.svg_-300x295.png 300w, https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Z-curve45.svg_-768x754.png 768w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Essentially, for a given XY integer coordinate, we take the binary representation of the X component, and the Y component, and then we interleave them together in order to get the resulting Z-order curve value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, take the case where we have an 8&#215;8, 2D grid. We have a point in the grid which we have determined falls within cell (3,5), so X=3 (011) and Y=5 (101).<\/p>\n\n\n\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-media-text is-stacked-on-mobile\" style=\"grid-template-columns:20% auto\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"179\" height=\"194\" src=\"https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/interleaving_z_order_value.png\" alt=\"\" class=\"wp-image-92 size-full\"\/><\/figure><div class=\"wp-block-media-text__content\">\n<p class=\"wp-block-paragraph\">To get a Z-order value, we need to interleave our binary X and Y values. So we take the lowest order bit of the X value and use it as the first (lowest order) bit of our binary Z-order curve value. For the next bit, we take the lowest order bit of the Y value. We then go back to the X value, but we take the next bit, then swap back to Y, etc., until we have our number.<\/p>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">After we interleave, we end up with a result of 100111 in binary, which is 39 in decimal. Imagine that we have 1 point within each cell of our 8&#215;8 grid. Once we have computed Z-order curve values for each point and sorted them from lowest to highest, our point (which has a value of 39) would be 40th in the list (don&#8217;t forget to count 0!). And if you look at the picture of a 6-bit Z-order curve above, you can see that if you count to the 40th position along it, the value is indeed 100111.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we have a 6-bit number, and we&#8217;re encoding 2 dimensions (X and Y) into our Z-order curve value, we can represent 8 possible coordinates along each axis, since we have 3 bits per axis, and with 3 bits we can represent the numbers 0-7.<\/p>\n\n\n\n<div class=\"wp-block-media-text has-media-on-the-right is-stacked-on-mobile\" style=\"grid-template-columns:auto 24%\"><div class=\"wp-block-media-text__content\">\n<p class=\"wp-block-paragraph\">For 3D, we use the same process, but we have to interleave our Z coordinate as well, so after we add each Y coordinate bit, the next highest order bit in our Z-order curve value will be a bit from our calculated Z coordinate.<\/p>\n<\/div><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"375\" height=\"349\" src=\"https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Lebesgue-3d-step2.png\" alt=\"\" class=\"wp-image-91 size-full\" srcset=\"https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Lebesgue-3d-step2.png 375w, https:\/\/3d-galaxy.com\/blog\/wp-content\/uploads\/2025\/12\/Lebesgue-3d-step2-300x279.png 300w\" sizes=\"auto, (max-width: 375px) 100vw, 375px\" \/><\/figure><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In 3D, it&#8217;s common to use a 32-bit integer to store a Z-order curve value. So that gives us 10 bits each, or 2<sup>10<\/sup> (1024) possible coordinates in each dimension (X, Y, and Z), since we have 30 bits available for 3D coordinate representation. If you really wanted to, you could encode an additional two values into the remaining 2 bits, giving you 2048 possible coordinates in both the X and Y dimensions (or however you wanna slice it), but we&#8217;re not gonna do that here. We&#8217;ll just leave the other 2 bits for our critter friends to chew on. \ud83d\udc07\ud83d\udc3f\ufe0f<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Z-Order Curve Value Computation Algorithm<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The general algorithm to determine the Z-order curve value for a 3D coordinate looks like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:.875rem;font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#222222\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#E6E6E6;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/\/The bounding box is the 3D area all of our positions should fall into. \n\/\/It isn't necessary to use a bounding box structure, but for the sake of \n\/\/simplicity, rather than passing in minimum and maximum X, Y, and \n\/\/Z values, it's recommended.\npublic uint GetZCurveValue(float3 point, BoundingBox bbox)\n{\n    uint result = 0;\n\n    \/\/Determine the normalized position of our point within \n    \/\/the bounded area (between 0.0 and 1.0)\n    \/\/We clamp the value to make sure that we stay within our \n    \/\/possible coordinate range (0-1024)\n    float3 normalizedPos = clamp((point - bbox.min) \/ (bbox.max - bbox.min), 0f, 0.999999f);\n\n    \/\/Assuming a 32 bit return value, we have 2^10 possibilities per dimension.\n    \/\/Calculate the approximate cell (out of 1024 in each dimension)\n    uint3 xyzCell = (uint3)(normalizedPos * 1023);\n\n    \/\/Loop through all 10 bits of the X, Y, and Z value and interleave them \n    \/\/into a single 32-bit int\n    int offset = 0;\n    for (int i = 0; i &lt; 10; i++)\n    {\n        \/\/Perform bitwise operations to interleave the values\n        \/\/First, shift each value to the right by i bits, then perform a bitwise AND with 1\n        \/\/Then shift the 1 or 0 to the left by the current number of offset bits\n        \/\/Finally, perform a bitwise OR to combine it with the result up to this point.\n        result |= ((xyzCell.x >> i) &amp; 1) &lt;&lt; offset;\n        result |= ((xyzCell.y >> i) &amp; 1) &lt;&lt; (offset + 1);\n        result |= ((xyzCell.z >> i) &amp; 1) &lt;&lt; (offset + 2);\n\n        \/\/Increment the offset by 3 so we can move to the next 3 interleaved bits\n        offset += 3;\n    }\n\n    return result;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-dark\" style=\"background-color: #222222\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #6A9955\">\/\/The bounding box is the 3D area all of our positions should fall into. <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/It isn&#39;t necessary to use a bounding box structure, but for the sake of <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/simplicity, rather than passing in minimum and maximum X, Y, and <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/Z values, it&#39;s recommended.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">public<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #569CD6\">uint<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">GetZCurveValue<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #4EC9B0\">float3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">point<\/span><span style=\"color: #E6E6E6\">, <\/span><span style=\"color: #4EC9B0\">BoundingBox<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #569CD6\">uint<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">result<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/Determine the normalized position of our point within <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/the bounded area (between 0.0 and 1.0)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/We clamp the value to make sure that we stay within our <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/possible coordinate range (0-1024)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #4EC9B0\">float3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">normalizedPos<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">clamp<\/span><span style=\"color: #E6E6E6\">((<\/span><span style=\"color: #9CDCFE\">point<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">min<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">\/<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">max<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">min<\/span><span style=\"color: #E6E6E6\">), <\/span><span style=\"color: #B5CEA8\">0f<\/span><span style=\"color: #E6E6E6\">, <\/span><span style=\"color: #B5CEA8\">0.999999f<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/Assuming a 32 bit return value, we have 2^10 possibilities per dimension.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/Calculate the approximate cell (out of 1024 in each dimension)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #4EC9B0\">uint3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #4EC9B0\">uint3<\/span><span style=\"color: #E6E6E6\">)(<\/span><span style=\"color: #9CDCFE\">normalizedPos<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">*<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1023<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/Loop through all 10 bits of the X, Y, and Z value and interleave them <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/into a single 32-bit int<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #569CD6\">int<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">offset<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #569CD6\">int<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #E6E6E6\">; <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #E6E6E6\">; <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\">++<\/span><span style=\"color: #E6E6E6\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">        \/\/Perform bitwise operations to interleave the values<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">        \/\/First, shift each value to the right by i bits, then perform a bitwise AND with 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">        \/\/Then shift the 1 or 0 to the left by the current number of offset bits<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">        \/\/Finally, perform a bitwise OR to combine it with the result up to this point.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">        <\/span><span style=\"color: #9CDCFE\">result<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|=<\/span><span style=\"color: #E6E6E6\"> ((<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&gt;&gt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">offset<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">        <\/span><span style=\"color: #9CDCFE\">result<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|=<\/span><span style=\"color: #E6E6E6\"> ((<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&gt;&gt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">offset<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">+<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">        <\/span><span style=\"color: #9CDCFE\">result<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|=<\/span><span style=\"color: #E6E6E6\"> ((<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">z<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&gt;&gt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">offset<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">+<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">        \/\/Increment the offset by 3 so we can move to the next 3 interleaved bits<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">        <\/span><span style=\"color: #9CDCFE\">offset<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">+=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">3<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">result<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">While this algorithm may be sufficient, there is an optimization which improves the speed by around 5-10x by using specific bit operations:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:.875rem;font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#222222\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#E6E6E6;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public uint GetZCurveValue(float3 point, BoundingBox bbox)\n{\n    float3 normalizedPos = clamp((point- bbox.min) \/ (bbox.max - bbox.min), 0f, 0.999999f);\n    uint3 xyzCell = (uint3)(normalizedPos * 1023);\n\n    \/\/Call the GetZCurveBitCoordinate function for each dimension, then\n    \/\/interleave the results by shifting the Y result over by 1 bit, and shifting \n    \/\/the Z result by two bits.\n    return GetZCurveBitCoordinate(xyzCell.x) | \n              (GetZCurveBitCoordinate(xyzCell.y) &lt;&lt; 1) | \n              (GetZCurveBitCoordinate(xyzCell.z) &lt;&lt; 2);\n}\n\n\/\/Sequentially shifts the bits of the provided one-dimensional coordinate \n\/\/until each representative bit is separated from all of the other bits using two zeros.\npublic uint GetZCurveBitCoordinate(uint value)\n{\n    value &amp;= 0x000003FF;                          \/\/00000000000000000000001111111111\n    value = (value | (value &lt;&lt; 16)) &amp; 0x030000FF; \/\/00000011000000000000000011111111\n    value = (value | (value &lt;&lt; 8)) &amp; 0x0300F00F;  \/\/00000011000000001111000000001111\n    value = (value | (value &lt;&lt; 4)) &amp; 0x030C30C3;  \/\/00000011000011000011000011000011\n    value = (value | (value &lt;&lt; 2)) &amp; 0x09249249;  \/\/00001001001001001001001001001001\n\n    return value;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-dark\" style=\"background-color: #222222\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">public<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #569CD6\">uint<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">GetZCurveValue<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #4EC9B0\">float3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">point<\/span><span style=\"color: #E6E6E6\">, <\/span><span style=\"color: #4EC9B0\">BoundingBox<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #4EC9B0\">float3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">normalizedPos<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">clamp<\/span><span style=\"color: #E6E6E6\">((<\/span><span style=\"color: #9CDCFE\">point<\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">min<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">\/<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">max<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">-<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">bbox<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">min<\/span><span style=\"color: #E6E6E6\">), <\/span><span style=\"color: #B5CEA8\">0f<\/span><span style=\"color: #E6E6E6\">, <\/span><span style=\"color: #B5CEA8\">0.999999f<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #4EC9B0\">uint3<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #4EC9B0\">uint3<\/span><span style=\"color: #E6E6E6\">)(<\/span><span style=\"color: #9CDCFE\">normalizedPos<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">*<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1023<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/Call the GetZCurveBitCoordinate function for each dimension, then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/interleave the results by shifting the Y result over by 1 bit, and shifting <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/the Z result by two bits.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">GetZCurveBitCoordinate<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">              (<\/span><span style=\"color: #DCDCAA\">GetZCurveBitCoordinate<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">              (<\/span><span style=\"color: #DCDCAA\">GetZCurveBitCoordinate<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #9CDCFE\">xyzCell<\/span><span style=\"color: #E6E6E6\">.<\/span><span style=\"color: #9CDCFE\">z<\/span><span style=\"color: #E6E6E6\">) <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #E6E6E6\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/Sequentially shifts the bits of the provided one-dimensional coordinate <\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/until each representative bit is separated from all of the other bits using two zeros.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">public<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #569CD6\">uint<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #DCDCAA\">GetZCurveBitCoordinate<\/span><span style=\"color: #E6E6E6\">(<\/span><span style=\"color: #569CD6\">uint<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&amp;=<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0x000003FF<\/span><span style=\"color: #E6E6E6\">;                          <\/span><span style=\"color: #6A9955\">\/\/00000000000000000000001111111111<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">16<\/span><span style=\"color: #E6E6E6\">)) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0x030000FF<\/span><span style=\"color: #E6E6E6\">; <\/span><span style=\"color: #6A9955\">\/\/00000011000000000000000011111111<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">8<\/span><span style=\"color: #E6E6E6\">)) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0x0300F00F<\/span><span style=\"color: #E6E6E6\">;  <\/span><span style=\"color: #6A9955\">\/\/00000011000000001111000000001111<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">4<\/span><span style=\"color: #E6E6E6\">)) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0x030C30C3<\/span><span style=\"color: #E6E6E6\">;  <\/span><span style=\"color: #6A9955\">\/\/00000011000011000011000011000011<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">|<\/span><span style=\"color: #E6E6E6\"> (<\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #D4D4D4\">&lt;&lt;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #E6E6E6\">)) <\/span><span style=\"color: #D4D4D4\">&amp;<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #B5CEA8\">0x09249249<\/span><span style=\"color: #E6E6E6\">;  <\/span><span style=\"color: #6A9955\">\/\/00001001001001001001001001001001<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">    <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #E6E6E6\"> <\/span><span style=\"color: #9CDCFE\">value<\/span><span style=\"color: #E6E6E6\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E6E6E6\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Once we&#8217;ve computed Z-order curve values for points, triangles, or anything else, we have a way to sort things and linearly search through them in an efficient way. The Z-order curve is great for building quadtrees, octrees, and other data structures.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u261d\ufe0f\ud83e\udd13 First off, Z-order curves have nothing to do with rendering order or Z-depth. Rather, such a &#8220;curve&#8221; is a useful mathematical tool for calculating a value, or code, for some entity, such as a point or a triangle, which approximates the relative position of that entity within some spatial grid. Entities which are closer [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-98","post","type-post","status-publish","format-standard","hentry","category-game-development"],"_links":{"self":[{"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=98"}],"version-history":[{"count":19,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98\/revisions"}],"predecessor-version":[{"id":513,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/98\/revisions\/513"}],"wp:attachment":[{"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=98"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=98"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/3d-galaxy.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=98"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}