void Start() { tfm = this.transform; cam = GetComponent <Camera>(); material = new Material(shader); cam.clearFlags = CameraClearFlags.Nothing; cells_order = new CellSortInfo[8]; node_stack = new NodeStackItem[32]; linked_idxyz = new IdXYZ[32]; for (int j = 0; j < node_stack.Length; j++) { node_stack[j] = new NodeStackItem(); // the 1st and the 7 following var idxyz0 = new IdXYZ(); var idxyz = idxyz0; for (int i = 1; i < 8; i++) { idxyz.next = new IdXYZ(); idxyz = idxyz.next; } linked_idxyz[j] = idxyz0; } stopwatch = new System.Diagnostics.Stopwatch(); }
static void RasterizeOctree(VoxelObject.OctreeNode node, float x0f, float y0f, float z, bool ortho, Color32 tint) { #region various initialization int stack_id = 0, stack_size = 1; var nsi = new NodeStackItem(); nsi.node = node; nsi.x = x0f; nsi.y = y0f; nsi.z = z; nsi.ortho = ortho; nsi.inside = false; node_stack[0] = nsi; VoxelObject.OctreeNode subnode = null; float lod_r = lod * 0.5f; float lod_factor = lod_r / persp_zoom_factor; float dk_ortho = ortho_switch_threshold * 0.5f; float depth_offset = -clip_near, depth_scale = ((1 << depth_buffer_bits) - 1) / (clip_far - clip_near); Color32 c = default(Color32); bool is_leaf = false; float xMinF = 0, xMaxF = 0, yMinF = 0, yMaxF = 0; int xMin = 0, xMax = 0, yMin = 0, yMax = 0; float BufW1f = BufW1 + 0.5f, BufH1f = BufH1 + 0.5f; float BufW1f1 = BufW1f - 1f, BufH1f1 = BufH1f - 1f; float proj_cx = 0, proj_cy = 0, proj_r = 0; #endregion branch_down :; bool switch_to_ortho = false; // int inside_flag = 0; ++processed_cells; var r = radiuses[stack_id]; float closest_z = nsi.z - r.w, farthest_z = nsi.z + r.w; if ((closest_z > clip_far) || (farthest_z < clip_near)) { goto branch_up; } else if (nsi.z < clip_near) { c = nsi.node.color; // a == 255 is considered a leaf if (c.a == 255) { goto branch_up; } else { goto skip_occlusion; } } if (nsi.ortho) { ++ortho_cells; c = nsi.node.color; // a == 255 is considered a leaf is_leaf = ((c.a == 255) || (r.z <= lod_r)); // r.z is max(r.x, r.y) // if intersects near plane -> no sense in trying to compute occlusion if (closest_z < clip_near) { if (!is_leaf) { goto skip_occlusion; // } else { // inside_flag = -1; } } xMinF = (nsi.x - r.x); xMaxF = (nsi.x + r.x); yMinF = (nsi.y - r.y); yMaxF = (nsi.y + r.y); } else { ++persp_cells; float closest_z_clamped; if (closest_z < clip_near) { closest_z_clamped = clip_near; } else { closest_z_clamped = closest_z; } c = nsi.node.color; // a == 255 is considered a leaf is_leaf = ((c.a == 255) || (r.w <= lod_factor * closest_z_clamped)); // if intersects near plane -> no sense in trying to compute occlusion if (closest_z < clip_near) { if (!is_leaf) { goto skip_occlusion; // } else { // inside_flag = -1; } } //float k_farthest = persp_zoom_factor / farthest_z; float k_closest = persp_zoom_factor / closest_z_clamped; float k_center = persp_zoom_factor / nsi.z; // If the min/max projections have less than 1 pixel difference, we can // switch to ortho. Ideally, this should check the difference between // closest and furthest, but seems like (closest - center) works fine too. switch_to_ortho = (!is_leaf) && ((k_closest - k_center) < dk_ortho); proj_cx = BufW2 + nsi.x * k_center; proj_cy = BufH2 + nsi.y * k_center; proj_r = (r.w * k_closest) - 0.5f; xMinF = (proj_cx - proj_r); xMaxF = (proj_cx + proj_r); yMinF = (proj_cy - proj_r); yMaxF = (proj_cy + proj_r); } if (xMinF < 0f) { xMinF = 0f; } if (xMaxF > BufW1f) { xMaxF = BufW1f; } if (yMinF < 0f) { yMinF = 0f; } if (yMaxF > BufH1f) { yMaxF = BufH1f; } // if (xMinF < 1f) { inside_flag = -1; } // if (xMaxF > BufW1f1) { inside_flag = -1; } // if (yMinF < 1f) { inside_flag = -1; } // if (yMaxF > BufH1f1) { inside_flag = -1; } xMin = (int)xMinF; xMax = (int)xMaxF; yMin = (int)yMinF; yMax = (int)yMaxF; #region rasterization / occlusion test if ((xMax < xMin) || (yMax < yMin)) { ++skipped_cells; goto branch_up; } else if ((xMax == xMin) && (yMax == yMin)) { int i = xMin + yMin * BufW; if (is_leaf) { float depth = nsi.z; //float depth = (nsi.z + depth_offset) * depth_scale; //int depth = (int)((nsi.z + depth_offset) * depth_scale); if (depth < depth_buffer[i]) { c.r = (byte)((c.r * tint.r + 255) >> 8); c.g = (byte)((c.g * tint.g + 255) >> 8); c.b = (byte)((c.b * tint.b + 255) >> 8); depth_buffer[i] = depth; color_buffer[i] = c; } ++pixel_cells; goto branch_up; } else { // nsi.inside = (inside_flag != -1); float depth = closest_z; //float depth = (closest_z + depth_offset) * depth_scale; //int depth = (int)((closest_z + depth_offset) * depth_scale); if (depth < depth_buffer[i]) { goto skip_occlusion; } ++skipped_cells; goto branch_up; } } else { int i_row = xMin + yMin * BufW; int row_width = (xMax - xMin); if (is_leaf) { float depth = nsi.z; //float depth = (nsi.z + depth_offset) * depth_scale; //int depth = (int)((nsi.z + depth_offset) * depth_scale); c.r = (byte)((c.r * tint.r + 255) >> 8); c.g = (byte)((c.g * tint.g + 255) >> 8); c.b = (byte)((c.b * tint.b + 255) >> 8); for (;;) { int i_row1 = i_row + row_width; int i = i_row; while (i <= i_row1) { if (depth < depth_buffer[i]) { depth_buffer[i] = depth; color_buffer[i] = c; } ++i; } if (++yMin > yMax) { ++leaf_cells; goto branch_up; } i_row += BufW; } } else { // nsi.inside = (inside_flag != -1); float depth = closest_z; //float depth = (closest_z + depth_offset) * depth_scale; //int depth = (int)((closest_z + depth_offset) * depth_scale); for (;;) { int i_row1 = i_row + row_width; int i = i_row; while (i <= i_row1) { if (depth < depth_buffer[i]) { goto skip_occlusion; } ++i; } if (++yMin > yMax) { ++skipped_cells; goto branch_up; } i_row += BufW; } } } #endregion skip_occlusion :; #region persp -> ortho switch // ortho switch is performed here to not do extra work if the node is occluded if (switch_to_ortho) { bool bbox_initialized = false; float b0x = 0, b0y = 0, b1y = 0, b1x = 0, b0k = 0, b1k = 0; var idxyz0 = linked_idxyz[stack_id]; var idxyz1 = linked_idxyz[linked_idxyz.Length - 1]; while (idxyz0 != null) { float wrk_x = nsi.x + (idxyz0.dx + idxyz0.dx); float wrk_y = nsi.y + (idxyz0.dy + idxyz0.dy); float wrk_z = nsi.z + (idxyz0.dz + idxyz0.dz); float wrk_k = persp_zoom_factor / wrk_z; float wrk_px = (BufW2 + wrk_x * wrk_k); float wrk_py = (BufH2 + wrk_y * wrk_k); if (bbox_initialized) { if (wrk_px < b0x) { b0x = wrk_px; } else if (wrk_px > b1x) { b1x = wrk_px; } if (wrk_py < b0y) { b0y = wrk_py; } else if (wrk_py > b1y) { b1y = wrk_py; } if (wrk_k < b0k) { b0k = wrk_k; } else if (wrk_k > b1k) { b1k = wrk_k; } } else { b0x = b1x = wrk_px; b0y = b1y = wrk_py; b0k = b1k = wrk_k; bbox_initialized = true; } idxyz1.dx = (wrk_px - proj_cx) * 0.5f; idxyz1.dy = (wrk_py - proj_cy) * 0.5f; idxyz1.dz = (wrk_z - nsi.z) * 0.5f; idxyz0 = idxyz0.next; idxyz1 = idxyz1.next; } // Make more precise check, otherwise there can be glitches. switch_to_ortho = ((b1k - b0k) < ortho_switch_threshold); if (switch_to_ortho) { switch_to_ortho = false; idxyz0 = linked_idxyz[stack_id]; idxyz1 = linked_idxyz[linked_idxyz.Length - 1]; while (idxyz0 != null) { idxyz0.dx = idxyz1.dx; idxyz0.dy = idxyz1.dy; idxyz0.dz = idxyz1.dz; idxyz0 = idxyz0.next; idxyz1 = idxyz1.next; } nsi.ortho = true; nsi.x = proj_cx; nsi.y = proj_cy; node_stack[stack_id] = nsi; stack_size = stack_id + 1; // invalidate cached values of perspective projection var rOrt = radiuses[stack_id]; rOrt.x = (b1x - b0x) * 0.5f; rOrt.y = (b1y - b0y) * 0.5f; rOrt.z = rOrt.x; if (rOrt.y > rOrt.z) { rOrt.z = rOrt.y; } rOrt.x -= 0.5f; rOrt.y -= 0.5f; // w (abs radius) stays the same radiuses[stack_id] = rOrt; } } #endregion ++nonleaf_cells; nsi.idxyz = linked_idxyz[stack_id]; resume_subnodes :; switch (nsi.idxyz.index) { case 0 : subnode = nsi.node.n000; break; case 1: subnode = nsi.node.n001; break; case 2: subnode = nsi.node.n010; break; case 3: subnode = nsi.node.n011; break; case 4: subnode = nsi.node.n100; break; case 5: subnode = nsi.node.n101; break; case 6: subnode = nsi.node.n110; break; case 7: subnode = nsi.node.n111; break; } if (subnode == null) { nsi.idxyz = nsi.idxyz.next; if (nsi.idxyz == null) { goto branch_up; } goto resume_subnodes; } IdXYZ idxyz = nsi.idxyz; nsi.idxyz = nsi.idxyz.next; node_stack[stack_id] = nsi; ++stack_id; if (stack_id == stack_size) { #region initialize next level's look-up tables from the current level r = radiuses[stack_id - 1]; r.x = r.x * 0.5f - 0.25f; r.y = r.y * 0.5f - 0.25f; r.z *= 0.5f; r.w *= 0.5f; radiuses[stack_id] = r; var idxyz0 = linked_idxyz[stack_id - 1]; var idxyz1 = linked_idxyz[stack_id]; while (idxyz0 != null) { idxyz1.index = idxyz0.index; idxyz1.dx = idxyz0.dx * 0.5f; idxyz1.dy = idxyz0.dy * 0.5f; idxyz1.dz = idxyz0.dz * 0.5f; idxyz0 = idxyz0.next; idxyz1 = idxyz1.next; } #endregion ++stack_size; } nsi.node = subnode; nsi.x += idxyz.dx; nsi.y += idxyz.dy; nsi.z += idxyz.dz; // if (nsi.ortho && nsi.inside) { // RasterizeOctreeInsideOrtho(nsi, stack_id, stack_size, tint); // goto branch_up; // } goto branch_down; branch_up :; if (stack_id == 0) { return; // EXIT POINT } bool old_ortho = nsi.ortho; nsi = node_stack[--stack_id]; if (nsi.ortho != old_ortho) { stack_size = stack_id + 1; // invalidate cached values of ortho projection } // (nsi.idxyz == null) means that all 8 subnodes were processed and we can return to the upper node if (nsi.idxyz == null) { goto branch_up; } else { goto resume_subnodes; } }
static void RasterizeOctreeInsideOrtho(NodeStackItem nsi, int stack_id, int stack_size, Color32 tint) { #region various initialization int stack_id_0 = stack_id; VoxelObject.OctreeNode subnode = null; float lod_r = lod * 0.5f; float depth_offset = -clip_near, depth_scale = ((1 << depth_buffer_bits) - 1) / (clip_far - clip_near); Color32 c = default(Color32); bool is_leaf = false; float xMinF = 0, xMaxF = 0, yMinF = 0, yMaxF = 0; int xMin = 0, xMax = 0, yMin = 0, yMax = 0; float BufW1f = BufW1 + 0.5f, BufH1f = BufH1 + 0.5f; #endregion branch_down :; ++processed_cells; var r = radiuses[stack_id]; float closest_z = nsi.z - r.w; { ++ortho_cells; c = nsi.node.color; // a == 255 is considered a leaf is_leaf = ((c.a == 255) || (r.z <= lod_r)); // r.z is max(r.x, r.y) xMinF = (nsi.x - r.x); xMaxF = (nsi.x + r.x); yMinF = (nsi.y - r.y); yMaxF = (nsi.y + r.y); } xMin = (int)xMinF; xMax = (int)xMaxF; yMin = (int)yMinF; yMax = (int)yMaxF; #region rasterization / occlusion test if ((xMax < xMin) || (yMax < yMin)) { ++skipped_cells; goto branch_up; } else if ((xMax == xMin) && (yMax == yMin)) { int i = xMin + yMin * BufW; if (is_leaf) { float depth = nsi.z; //float depth = (nsi.z + depth_offset) * depth_scale; //int depth = (int)((nsi.z + depth_offset) * depth_scale); if (depth < depth_buffer[i]) { c.r = (byte)((c.r * tint.r + 255) >> 8); c.g = (byte)((c.g * tint.g + 255) >> 8); c.b = (byte)((c.b * tint.b + 255) >> 8); depth_buffer[i] = depth; color_buffer[i] = c; } ++pixel_cells; goto branch_up; } else { float depth = closest_z; //float depth = (closest_z + depth_offset) * depth_scale; //int depth = (int)((closest_z + depth_offset) * depth_scale); if (depth < depth_buffer[i]) { goto skip_occlusion; } ++skipped_cells; goto branch_up; } } else { int i_row = xMin + yMin * BufW; int row_width = (xMax - xMin); if (is_leaf) { float depth = nsi.z; //float depth = (nsi.z + depth_offset) * depth_scale; //int depth = (int)((nsi.z + depth_offset) * depth_scale); c.r = (byte)((c.r * tint.r + 255) >> 8); c.g = (byte)((c.g * tint.g + 255) >> 8); c.b = (byte)((c.b * tint.b + 255) >> 8); for (;;) { int i_row1 = i_row + row_width; int i = i_row; while (i <= i_row1) { if (depth < depth_buffer[i]) { depth_buffer[i] = depth; color_buffer[i] = c; } ++i; } if (++yMin > yMax) { ++leaf_cells; goto branch_up; } i_row += BufW; } } else { float depth = closest_z; //float depth = (closest_z + depth_offset) * depth_scale; //int depth = (int)((closest_z + depth_offset) * depth_scale); for (;;) { int i_row1 = i_row + row_width; int i = i_row; while (i <= i_row1) { if (depth < depth_buffer[i]) { goto skip_occlusion; } ++i; } if (++yMin > yMax) { ++skipped_cells; goto branch_up; } i_row += BufW; } } } #endregion skip_occlusion :; ++nonleaf_cells; nsi.idxyz = linked_idxyz[stack_id]; resume_subnodes :; switch (nsi.idxyz.index) { case 0 : subnode = nsi.node.n000; break; case 1: subnode = nsi.node.n001; break; case 2: subnode = nsi.node.n010; break; case 3: subnode = nsi.node.n011; break; case 4: subnode = nsi.node.n100; break; case 5: subnode = nsi.node.n101; break; case 6: subnode = nsi.node.n110; break; case 7: subnode = nsi.node.n111; break; } if (subnode == null) { nsi.idxyz = nsi.idxyz.next; if (nsi.idxyz == null) { goto branch_up; } goto resume_subnodes; } IdXYZ idxyz = nsi.idxyz; nsi.idxyz = nsi.idxyz.next; node_stack[stack_id] = nsi; ++stack_id; if (stack_id == stack_size) { #region initialize next level's look-up tables from the current level r = radiuses[stack_id - 1]; r.x = r.x * 0.5f - 0.25f; r.y = r.y * 0.5f - 0.25f; r.z *= 0.5f; r.w *= 0.5f; radiuses[stack_id] = r; var idxyz0 = linked_idxyz[stack_id - 1]; var idxyz1 = linked_idxyz[stack_id]; while (idxyz0 != null) { idxyz1.index = idxyz0.index; idxyz1.dx = idxyz0.dx * 0.5f; idxyz1.dy = idxyz0.dy * 0.5f; idxyz1.dz = idxyz0.dz * 0.5f; idxyz0 = idxyz0.next; idxyz1 = idxyz1.next; } #endregion ++stack_size; } nsi.node = subnode; nsi.x += idxyz.dx; nsi.y += idxyz.dy; nsi.z += idxyz.dz; goto branch_down; branch_up :; if (stack_id <= stack_id_0) { return; // EXIT POINT } bool old_ortho = nsi.ortho; nsi = node_stack[--stack_id]; if (nsi.ortho != old_ortho) { stack_size = stack_id + 1; // invalidate cached values of ortho projection } // (nsi.idxyz == null) means that all 8 subnodes were processed and we can return to the upper node if (nsi.idxyz == null) { goto branch_up; } else { goto resume_subnodes; } }