/// <summary> /// Initializes and divides tree /// </summary> /// <param name="depthDivision">Depth of division</param> /// <param name="transformParent">Parent for tree trunk GameObject</param> public void InitializeTree(int depthDivision, Transform transformParent = null) { trunk = new OctreeNode(); trunk.gameObject = new GameObject(); trunk.gameObject.transform.parent = transformParent; trunk.AABB = new Bounds(Vector3.zero, new Vector3(4, 4, 4)); DivideTree(2); InitializeDeepestNodesValue(); }
public void Build(MeshDesc desc, OctreeNode root) { desc.Clear(); meshDesc = desc; if (root != null) { GenerateVertices(root); ContourCellProc(root); } }
/// <summary> /// Recursively divides tree /// </summary> /// <param name="n">Current node</param> /// <param name="depth">Depth of division</param> /// <param name="_deepestNodes">List to add deepest nodes to</param> private void DivideTreeNode(OctreeNode n, int depth, List<OctreeNode> _deepestNodes) { var parentBB = n.AABB; OctreeNode t; Vector3 e = parentBB.extents /= 2; n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(e.x, e.y, e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(-e.x, e.y, e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(e.x, -e.y, e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(-e.x, -e.y, e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(e.x, e.y, -e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(-e.x, e.y, -e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(e.x, -e.y, -e.z); n.children.Add(t = new OctreeNode()); t.AABB = parentBB; t.AABB.center += new Vector3(-e.x, -e.y, -e.z); for (int i = 0; i < n.children.Count; i++) { t = n.children[i]; t.parent = n; t.gameObject = new GameObject(); t.gameObject.transform.position = t.AABB.center; t.gameObject.transform.localScale = t.AABB.size; t.gameObject.transform.parent = n.gameObject.transform; } if (depth-- > 0) for (int i = 0; i < n.children.Count; i++) DivideTreeNode(n.children[i], depth, _deepestNodes); else _deepestNodes.AddRange(n.children); }
void DrawNode(OctreeNode node) { switch (mode) { case Mode.Hierarchy: DrawNode_HierarchyMode(node); break; case Mode.Edges: DrawNode_EdgesMode(node); break; } }
void DrawNode_EdgesMode(OctreeNode node) { const float dotSize = 0.01f; if (node.type == OctreeNode.Type.Leaf) { Vector3 nodeSize = new Vector3(size, size, size) * node.size; Vector3 center = node.min + nodeSize * 0.5f; Gizmos.DrawWireCube(center, nodeSize); Gizmos.DrawCube(node.info.position, new Vector3(dotSize, dotSize, dotSize)); } }
public Octree(BoundingBox bounds, int maxDepth, int maxObjectsPerNode, int minObjectsPerNode) { Bounds = bounds; MaxDepth = maxDepth; MaxObjectsPerNode = maxObjectsPerNode; MinObjectsPerNode = minObjectsPerNode; Root = new OctreeNode(Bounds, this, 0, null); DebugDraw = false; Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); ObjectsToNodes = new Dictionary<IBoundedObject, OctreeNode>(); ObjectsToUpdate = new Dictionary<IBoundedObject, bool>(); UpdateTimer = new Timer(1.0f, false); }
void DrawNode_HierarchyMode(OctreeNode node) { Vector3 nodeSize = new Vector3(size, size, size) * node.size; Vector3 center = node.min + nodeSize * 0.5f; switch (node.type) { case OctreeNode.Type.Leaf: Gizmos.DrawCube(center, nodeSize); break; case OctreeNode.Type.Internal: Gizmos.DrawWireCube(center, nodeSize); break; } }
public static void Build(OctreeNode node, int minSize) { if (node == null) return; if (node.size < minSize) return; int childSize = node.size / 2; for (int i = 0; i < 8; ++i) { OctreeNode child = new OctreeNode(); child.type = OctreeNode.Type.Internal; child.size = childSize; child.min = node.min + (CHILD_MIN_OFFSETS[i] * childSize); Build(child, minSize); node.children[i] = child; } }
void ContourCellProc(OctreeNode node) { if (node.type == OctreeNode.Type.Internal) { for (int i = 0; i < node.children.Length; ++i) { OctreeNode child = node.children[i]; if (child != null) ContourCellProc(child); } for (int i = 0; i < 12; ++i) { OctreeNode[] faceNodes = new OctreeNode[2]; int[] c = new int[2]{ cellProcFaceMask[i, 0], cellProcFaceMask[i, 1] }; faceNodes[0] = node.children[c[0]]; faceNodes[1] = node.children[c[1]]; ContourFaceProc(faceNodes, cellProcFaceMask[i, 2]); } for (int i = 0; i < 6; ++i) { OctreeNode[] edgeNodes = new OctreeNode[4]; int[] c = new int[4] { cellProcEdgeMask[i, 0], cellProcEdgeMask[i, 1], cellProcEdgeMask[i, 2], cellProcEdgeMask[i, 3], }; for (int j = 0; j < 4; ++j) { edgeNodes[j] = node.children[c[j]]; } ContourEdgeProc(edgeNodes, cellProcEdgeMask[i, 4]); } } }
public Window() : base(1280, 720, new GraphicsMode(32, 0, 0, 4), "OpenCAD") { stl = new STL("Models/elephant.stl", Color.Green, STLType.Binary); var s1 = new Sphere {Center = Vect3.Zero, Radius = 4}; var s2 = new Sphere {Center = new Vect3(0,5,0), Radius = 4}; var t1 = new Octree<Voxel>(Vect3.Zero, 32.0); var t2 = new Octree<Voxel>(Vect3.Zero, 32.0); Test2(t1, node => s1.Intersects(node.AABB)); Test(t2, stl.Elements); _tree = t1.Union(t2); //_tree.Test(node => sphere.Intersects(node.AABB),maxLevel); //Test2(t, node => sphere.Intersects(node.AABB)); //t[0].Clear(); //t[0].Clear(); //Test(_tree,stl.Elements); //create from stl //foreach (var tri in stl.Elements) //{ // Intersect(_tree, tri); //} VSync = VSyncMode.On; _camera = new Camera(); Mouse.WheelChanged += (sender, args) => { _camera.View = _camera.View * Mat4.Translate(0, 0, args.DeltaPrecise * -10.0); //_camera.Eye += new Vect3(0, 0, args.DeltaPrecise * -10.0); // Console.WriteLine(_camera.Eye); }; }
public static OctreeChildCoords FromIndex(OctreeNode.ChildIndex index) { switch (index) { case OctreeNode.ChildIndex.LeftBelowBack: return new OctreeChildCoords(0, 0, 0); case OctreeNode.ChildIndex.LeftBelowForward: return new OctreeChildCoords(0, 0, 1); case OctreeNode.ChildIndex.LeftAboveBack: return new OctreeChildCoords(0, 1, 0); case OctreeNode.ChildIndex.LeftAboveForward: return new OctreeChildCoords(0, 1, 1); case OctreeNode.ChildIndex.RightBelowBack: return new OctreeChildCoords(1, 0, 0); case OctreeNode.ChildIndex.RightBelowForward: return new OctreeChildCoords(1, 0, 1); case OctreeNode.ChildIndex.RightAboveBack: return new OctreeChildCoords(1, 1, 0); case OctreeNode.ChildIndex.RightAboveForward: return new OctreeChildCoords(1, 1, 1); default: throw new ArgumentOutOfRangeException("index", index, null); } }
/// <summary> /// Reduce the depth of the tree /// </summary> public void reduce() { int index; for (index = _maxColorBits - 1; (index > 0) && (null == _reducibleNodes[index]); ++index) ; OctreeNode node = _reducibleNodes[index]; _reducibleNodes[index] = node.nextReducible; _leafCount -= node.reduce(); _previousNode = null; }
/// <summary> /// Keep track of the previous node that was quantized /// </summary> /// <param name="node">The node last quantized</param> protected void TrackPrevious(OctreeNode node) { _previousNode = node; }
protected void TrackPrevious(OctreeNode node) => this.previousNode = node;
/// /// <summary> /// Add items to the Octree /// </summary> /// /// <param name="itemPositions"> /// The positions (centroid) of the items being added /// </param> /// /// <param name="itemBoundingBoxes"> /// The bounding boxes for each item being added /// </param> /// /// <param name="searchRoot"> /// The node to attempt to add the items within /// </param> /// private void AddItems(Vector3[] itemPositions, BoundingBox[] itemBoundingBoxes, OctreeNode searchRoot) { if (itemPositions.Length != itemBoundingBoxes.Length) { throw new ArgumentException("there must be as many item positions as there are item bounding boxes"); } for (int i = 0; i < itemPositions.Length; ++i) { List <OctreeNode> nodes = FindContainingNodes(searchRoot, itemBoundingBoxes[i]); foreach (OctreeNode node in nodes) { if (node.InternalObjectPositions.Count + 1 >= _maximumObjectsInNode) { BreakDownNode(node); List <OctreeNode> newNodes = FindContainingNodes(node, itemBoundingBoxes[i]); foreach (OctreeNode newNode in newNodes) { // These nodes will be brand new, so we can always add 1 item to them without checks newNode.AddObject(itemPositions[i], itemBoundingBoxes[i]); } } else { node.AddObject(itemPositions[i], itemBoundingBoxes[i]); } } } }
private List <OctreeNode> ComputeVoxels(ComputeShader shader, Vector3 min, int octreeSize) { float gpuStart = Time.realtimeSinceStartup; float[] chunkPos = new float[3] { min.x, min.y, min.z }; shader.SetFloats("chunkPosition", chunkPos); shader.SetInt("resolution", octreeSize); shader.SetInt("octreeSize", octreeSize); float sqRTRC = Mathf.Sqrt(octreeSize * octreeSize * octreeSize); int sqRTRes = (int)sqRTRC; if (sqRTRC > sqRTRes) { sqRTRes++; } ComputeBuffer Perm = new ComputeBuffer(512, sizeof(int)); Perm.SetData(permutations); ComputeBuffer cornCount = new ComputeBuffer(sqRTRes, sizeof(int)); ComputeBuffer finalCount = new ComputeBuffer(1, sizeof(float)); ComputeBuffer voxMatBuffer = new ComputeBuffer(octreeSize * octreeSize * octreeSize, sizeof(uint)); float rD8 = octreeSize / 8.0f; int rD8I = (int)rD8; if (rD8 > rD8I) { rD8I++; } int kernel = shader.FindKernel("ComputeCorners"); shader.SetBuffer(kernel, "Perm", Perm); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.Dispatch(kernel, rD8I, rD8I, rD8I); /*kernel = shader.FindKernel("ComputeLength"); * shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); * shader.SetBuffer(kernel, "cornerCount", cornCount); * shader.Dispatch(kernel, 1, 1, 1);*/ kernel = shader.FindKernel("AddLength"); shader.SetBuffer(kernel, "cornerCount", cornCount); shader.SetBuffer(kernel, "finalCount", finalCount); shader.Dispatch(kernel, 1, 1, 1); float[] voxelCount = new float[1]; finalCount.GetData(voxelCount); finalCount.SetData(voxelCount); int count = (int)voxelCount[0]; //Debug.Log (count); if (count <= 0) { voxMatBuffer.Dispose(); cornCount.Dispose(); finalCount.Dispose(); Perm.Dispose(); return(null); } ComputeBuffer cornerIndexes = new ComputeBuffer(count, sizeof(uint)); kernel = shader.FindKernel("ComputePositions"); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.SetBuffer(kernel, "cornerCount", cornCount); shader.SetBuffer(kernel, "cornerIndexes", cornerIndexes); shader.Dispatch(kernel, 1, 1, 1); ComputeBuffer voxBuffer = new ComputeBuffer(count, (sizeof(float) * 6) + sizeof(int)); ComputeBuffer positionBuffer = new ComputeBuffer(count, sizeof(float) * 3); kernel = shader.FindKernel("ComputeVoxels"); shader.SetBuffer(kernel, "Perm", Perm); shader.SetBuffer(kernel, "voxMins", positionBuffer); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.SetBuffer(kernel, "finalCount", finalCount); shader.SetBuffer(kernel, "cornerIndexes", cornerIndexes); shader.SetBuffer(kernel, "voxels", voxBuffer); //int dispatchCount = count / 10; shader.Dispatch(kernel, (count / 128) + 1, 1, 1); List <OctreeNode> computedVoxels = new List <OctreeNode>(); Vector3[] voxelMins = new Vector3[count]; positionBuffer.GetData(voxelMins); positionBuffer.Dispose(); uint[] voxelMaterials = new uint[count]; cornerIndexes.GetData(voxelMaterials); cornerIndexes.Dispose(); GPUVOX[] voxs = new GPUVOX[count]; voxBuffer.GetData(voxs); voxBuffer.Dispose(); voxMatBuffer.Dispose(); cornCount.Dispose(); finalCount.Dispose(); Perm.Dispose(); float gpuEnd = Time.realtimeSinceStartup; //Debug.Log ("GPU time on chunk: " + (gpuEnd - gpuStart)); int HIGHEST_VOXEL_RES = 64; int voxelSize = HIGHEST_VOXEL_RES / octreeSize; for (int i = 0; i < count; i++) { if (voxs[i].numPoints != 0) { OctreeNode leaf = new OctreeNode(); leaf.type = OctreeNodeType.Node_Leaf; leaf.size = voxelSize; OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.position = voxs[i].vertPoint; drawInfo.averageNormal = voxs[i].avgNormal; drawInfo.corners = (int)voxelMaterials[i]; leaf.drawInfo = drawInfo; leaf.min = voxelMins[i]; computedVoxels.Add(leaf); } } //Debug.Log ("CPU Leaf generation time on chunk: " + (Time.realtimeSinceStartup - gpuEnd)); //Debug.Log (computedVoxels.Count); return(computedVoxels); }
public virtual void Awake() { rootNode = new OctreeNode(new Vector3Int(-2, -2, -2), 8); }
//costruisce il nodo terminale (foglia) calcolando le NodeInfo per la generazione della mesh public OctreeNode BuildLeaf(OctreeNode leaf, int size) { if (leaf == null || leaf.size != size) { return(null); } int corners = 0; for (int i = 0; i < 8; i++) { Vector3 cornerPos = leaf.min + (CHILD_MIN_OFFSETS[i] * size); float density = Density.DensityFunc(cornerPos); int material = density < 0.0f ? SOLID : AIR; corners |= (material << i); } if (corners == 0 || corners == 255) { leaf = null; return(null); } int edgeCount = 0; Vector3 averageNormal = new Vector3(); QefSolver qef = new QefSolver(); for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) { int c1 = edgevmap[i][0]; int c2 = edgevmap[i][1]; int m1 = (corners >> c1) & 1; int m2 = (corners >> c2) & 1; if ((m1 == AIR && m2 == AIR) || (m1 == SOLID && m2 == SOLID)) { continue; } Vector3 p1 = leaf.min + (CHILD_MIN_OFFSETS[c1] * size); Vector3 p2 = leaf.min + (CHILD_MIN_OFFSETS[c2] * size); Vector3 p = ApproximateZeroCrossingPosition(p1, p2, 8); Vector3 n = CalculateSurfaceNormal(p); qef.Add(p.x, p.y, p.z, n.x, n.y, n.z); averageNormal += n; edgeCount++; } Vector3 qefPosition = new Vector3(); qef.Solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); NodeInfo info = new NodeInfo(); info.index = -1; info.corners = 0; info.position = qefPosition; info.qef = qef.GetData(); Vector3 min = leaf.min; Vector3 max = new Vector3(leaf.min.x + leaf.size, leaf.min.y + leaf.size, leaf.min.z + leaf.size); if (info.position.x < min.x || info.position.x > max.x || info.position.y < min.y || info.position.y > max.y || info.position.z < min.z || info.position.z > max.z) { info.position = qef.GetMassPoint(); } info.avgNormal = (averageNormal / (float)edgeCount).normalized; info.corners = corners; leaf.type = NodeType.LEAF; leaf.nodeInfo = info; return(leaf); }
/// <summary> /// Return a color palette for the computed octree. /// </summary> /// <returns>A color palette for the computed octree</returns> internal Color[] GetPaletteColors() { // If we haven't already computed it, compute it now if (this.octreePalette == null) { // Start at the second-to-last reducible level int reductionLevel = this.octreeReducibleNodes.Length - 1; // We want to subtract one from the target if we have a transparent // bit because we want to save room for that special color int targetCount = this.octreeMaxColors - (this.octreeHasTransparent ? 1 : 0); // While we still have more colors than the target... while (this.octreeColorCount > targetCount) { // Find the first reducible node, starting with the last level // that can have reducible nodes while (reductionLevel > 0 && this.octreeReducibleNodes[reductionLevel] == null) { --reductionLevel; } if (this.octreeReducibleNodes[reductionLevel] == null) { // Shouldn't get here break; } // We should have a node now OctreeNode newLeaf = this.octreeReducibleNodes[reductionLevel]; this.octreeReducibleNodes[reductionLevel] = newLeaf.NextReducibleNode; this.octreeColorCount -= newLeaf.Reduce() - 1; } if (reductionLevel == 0 && !this.octreeHasTransparent) { // If this was the top-most level, we now only have a single color // representing the average. That's not what we want. // use just black and white this.octreePalette = new Color[2]; this.octreePalette[0] = Color.Black; this.octreePalette[1] = Color.White; // And empty the octree so it always picks the closer of the black and white entries this.octreeRoot = new OctreeNode(0, this); } else { // Now walk the tree, adding all the remaining colors to the list int paletteIndex = 0; this.octreePalette = new Color[this.octreeColorCount + (this.octreeHasTransparent ? 1 : 0)]; // Add the transparent color if we need it if (this.octreeHasTransparent) { this.octreePalette[paletteIndex++] = Color.Transparent; } // Have the nodes insert their leaf colors this.octreeRoot.AddColorsToPalette(this.octreePalette, ref paletteIndex); } } return(this.octreePalette); }
/// \cond protected override bool SynchronizeOctree(uint availableSyncOperations) { VolumeRenderer volumeRenderer = gameObject.GetComponent <VolumeRenderer>(); VolumeCollider volumeCollider = gameObject.GetComponent <VolumeCollider>(); Vector3 camPos = CameraUtils.getCurrentCameraPosition(); // This is messy - perhaps the LOD thresold shold not be a parameter to update. Instead it could be passed // as a parameter during traversal, so different traversal could retrieve differnt LODs. We then wouldn't // want a single 'renderThisNode' member of Cubiquity nodes, but instead some threshold we could compare to. float lodThreshold = GetComponent <VolumeRenderer>() ? GetComponent <VolumeRenderer>().lodThreshold : 1.0f; int minimumLOD = GetComponent <VolumeRenderer>() ? GetComponent <VolumeRenderer>().minimumLOD : 0; // Although the LOD system is partially functional I don't feel it's ready for release yet. // The following line disables it by forcing the highest level of detail to always be used. minimumLOD = 0; // Next line commented out so the system starts up with LOD disabled. //if (volumeRenderer.hasChanged) { CubiquityDLL.SetLodRange(data.volumeHandle.Value, minimumLOD, 0); } bool cubiquityUpToDate = CubiquityDLL.UpdateVolume(data.volumeHandle.Value, camPos.x, camPos.y, camPos.z, lodThreshold); if (CubiquityDLL.HasRootOctreeNode(data.volumeHandle.Value) == 1) { uint rootNodeHandle = CubiquityDLL.GetRootOctreeNode(data.volumeHandle.Value); if (rootOctreeNodeGameObject == null) { rootOctreeNodeGameObject = OctreeNode.CreateOctreeNode(rootNodeHandle, gameObject); } OctreeNode.syncNode(ref availableSyncOperations, rootOctreeNodeGameObject, rootNodeHandle, gameObject); if (volumeRenderer != null && volumeRenderer.hasChanged) { OctreeNode.syncNodeWithVolumeRenderer(rootOctreeNodeGameObject, volumeRenderer, true); } if (volumeCollider != null && volumeCollider.hasChanged) { OctreeNode.syncNodeWithVolumeCollider(rootOctreeNodeGameObject, volumeCollider, true); } } // These properties might have to be synced with the volume (e.g. LOD settings) or with components // (e.g. shadow/material settings). Therefore we don't clear the flags until all syncing is completed. if (volumeRenderer != null) { volumeRenderer.hasChanged = false; } if (volumeCollider != null) { volumeCollider.hasChanged = false; } // If there were still sync operations available then there was no more syncing to be done with the // Cubiquity octree. So if the Cubiquity octree was also up to date then we have synced everything. return(cubiquityUpToDate && availableSyncOperations > 0); }
public static void syncNode(ref uint availableSyncOperations, GameObject nodeGameObject, uint nodeHandle, GameObject voxelTerrainGameObject) { OctreeNode octreeNode = nodeGameObject.GetComponent <OctreeNode>(); CuOctreeNode cuOctreeNode = CubiquityDLL.GetOctreeNode(nodeHandle); //////////////////////////////////////////////////////////////////////////////// // Has anything in this node or its children changed? If so, we may need to syncronise the node's properties, mesh and // structure. Each of these can be tested against a timestamp. We may also need to do this recursively on child nodes. //////////////////////////////////////////////////////////////////////////////// if (cuOctreeNode.nodeOrChildrenLastChanged > octreeNode.nodeAndChildrenLastSynced) { bool resyncedProperties = false; // See comments where this is tested - it's a bit of a hack //////////////////////////////////////////////////////////////////////////////// // 1st test - Have the properties of the node changed? //////////////////////////////////////////////////////////////////////////////// if (cuOctreeNode.propertiesLastChanged > octreeNode.propertiesLastSynced) { octreeNode.renderThisNode = cuOctreeNode.renderThisNode != 0; octreeNode.height = cuOctreeNode.height; octreeNode.propertiesLastSynced = CubiquityDLL.GetCurrentTime(); resyncedProperties = true; } //////////////////////////////////////////////////////////////////////////////// // 2nd test - Has the mesh changed and do we have time to syncronise it? //////////////////////////////////////////////////////////////////////////////// if ((cuOctreeNode.meshLastChanged > octreeNode.meshLastSynced) && (availableSyncOperations > 0)) { if (cuOctreeNode.hasMesh == 1) { // Set up the rendering mesh VolumeRenderer volumeRenderer = voxelTerrainGameObject.GetComponent <VolumeRenderer>(); if (volumeRenderer != null) { MeshFilter meshFilter = nodeGameObject.GetOrAddComponent <MeshFilter>() as MeshFilter; if (meshFilter.sharedMesh == null) { meshFilter.sharedMesh = new Mesh(); } MeshRenderer meshRenderer = nodeGameObject.GetOrAddComponent <MeshRenderer>() as MeshRenderer; if (voxelTerrainGameObject.GetComponent <Volume>().GetType() == typeof(TerrainVolume)) { MeshConversion.BuildMeshFromNodeHandleForTerrainVolume(meshFilter.sharedMesh, nodeHandle, false); } else if (voxelTerrainGameObject.GetComponent <Volume>().GetType() == typeof(ColoredCubesVolume)) { MeshConversion.BuildMeshFromNodeHandleForColoredCubesVolume(meshFilter.sharedMesh, nodeHandle, false); } meshRenderer.enabled = volumeRenderer.enabled && octreeNode.renderThisNode; // For syncing materials, shadow properties, etc. syncNodeWithVolumeRenderer(nodeGameObject, volumeRenderer, false); } // Set up the collision mesh VolumeCollider volumeCollider = voxelTerrainGameObject.GetComponent <VolumeCollider>(); if (volumeCollider != null) { bool useCollider = volumeCollider.useInEditMode || Application.isPlaying; if (useCollider) { // I'm not quite comfortable with this. For some reason we have to create this new mesh, fill it, // and set it as the collider's shared mesh, whereas I would rather just pass the collider's sharedMesh // straight to the functon that fills it. For some reason that doesn't work properly, and we see // issues with objects falling through terrain or not updating when part of the terrain is deleted. // It's to be investigated further... perhaps we could try deleting and recreating the MeshCollider? // Still, the approach below seems to work properly. Mesh collisionMesh = new Mesh(); if (voxelTerrainGameObject.GetComponent <Volume>().GetType() == typeof(TerrainVolume)) { MeshConversion.BuildMeshFromNodeHandleForTerrainVolume(collisionMesh, nodeHandle, true); } else if (voxelTerrainGameObject.GetComponent <Volume>().GetType() == typeof(ColoredCubesVolume)) { MeshConversion.BuildMeshFromNodeHandleForColoredCubesVolume(collisionMesh, nodeHandle, true); } MeshCollider meshCollider = nodeGameObject.GetOrAddComponent <MeshCollider>() as MeshCollider; meshCollider.sharedMesh = collisionMesh; } } } // If there is no mesh in Cubiquity then we make sure there isn't one in Unity. else { MeshCollider meshCollider = nodeGameObject.GetComponent <MeshCollider>() as MeshCollider; if (meshCollider) { Utility.DestroyOrDestroyImmediate(meshCollider); } MeshRenderer meshRenderer = nodeGameObject.GetComponent <MeshRenderer>() as MeshRenderer; if (meshRenderer) { Utility.DestroyOrDestroyImmediate(meshRenderer); } MeshFilter meshFilter = nodeGameObject.GetComponent <MeshFilter>() as MeshFilter; if (meshFilter) { Utility.DestroyOrDestroyImmediate(meshFilter); } } octreeNode.meshLastSynced = CubiquityDLL.GetCurrentTime(); availableSyncOperations--; } // We want to syncronize the properties before the mesh, so that the enabled flag can be set correctly when the mesh // is created. But we also want to syncronize properties after the mesh, so we can apply the correct enabled flag to // existing meshes when the node's 'renderThisNode' flag has changed. Therefore we set the 'resyncedProperties' flag // previously to let ourseves know that we should come back an finish the propertiy syncing here. It's a bit of a hack. if (resyncedProperties) { VolumeRenderer volumeRenderer = voxelTerrainGameObject.GetComponent <VolumeRenderer>(); if (volumeRenderer != null) { syncNodeWithVolumeRenderer(nodeGameObject, volumeRenderer, false); } VolumeCollider volumeCollider = voxelTerrainGameObject.GetComponent <VolumeCollider>(); if (volumeCollider != null) { syncNodeWithVolumeCollider(nodeGameObject, volumeCollider, false); } } uint[,,] childHandleArray = new uint[2, 2, 2]; childHandleArray[0, 0, 0] = cuOctreeNode.childHandle000; childHandleArray[0, 0, 1] = cuOctreeNode.childHandle001; childHandleArray[0, 1, 0] = cuOctreeNode.childHandle010; childHandleArray[0, 1, 1] = cuOctreeNode.childHandle011; childHandleArray[1, 0, 0] = cuOctreeNode.childHandle100; childHandleArray[1, 0, 1] = cuOctreeNode.childHandle101; childHandleArray[1, 1, 0] = cuOctreeNode.childHandle110; childHandleArray[1, 1, 1] = cuOctreeNode.childHandle111; //////////////////////////////////////////////////////////////////////////////// // 3rd test - Has the structure of the octree node changed (gained or lost children)? //////////////////////////////////////////////////////////////////////////////// if (cuOctreeNode.structureLastChanged > octreeNode.structureLastSynced) { //Now syncronise any children for (uint z = 0; z < 2; z++) { for (uint y = 0; y < 2; y++) { for (uint x = 0; x < 2; x++) { if (childHandleArray[x, y, z] != 0xFFFFFFFF) { uint childNodeHandle = childHandleArray[x, y, z]; if (octreeNode.GetChild(x, y, z) == null) { octreeNode.SetChild(x, y, z, OctreeNode.CreateOctreeNode(childNodeHandle, nodeGameObject)); } } else { if (octreeNode.GetChild(x, y, z)) { Utility.DestroyOrDestroyImmediate(octreeNode.GetChild(x, y, z)); octreeNode.SetChild(x, y, z, null); } } } } } octreeNode.structureLastSynced = CubiquityDLL.GetCurrentTime(); } //////////////////////////////////////////////////////////////////////////////// // The last step of syncronization is to apply it recursively to our children. //////////////////////////////////////////////////////////////////////////////// for (uint z = 0; z < 2; z++) { for (uint y = 0; y < 2; y++) { for (uint x = 0; x < 2; x++) { if (octreeNode.GetChild(x, y, z) != null && availableSyncOperations > 0) { OctreeNode.syncNode(ref availableSyncOperations, octreeNode.GetChild(x, y, z), childHandleArray[x, y, z], voxelTerrainGameObject); } } } } // We've reached the end of our syncronization process. If there are still sync operations available then // we did less work then we could have, which implies we finished. Therefore mark the whole tree as synced. if (availableSyncOperations > 0) { octreeNode.nodeAndChildrenLastSynced = CubiquityDLL.GetCurrentTime(); } } }
void FinishUpChunkLoading() { if (finishedChunk) { finishedChunk = false; busy = false; if (changingChunk == null) { if (thread.m_Root != null && loadingChunk != null) { //if (loadingChunk.LOD == 6) //Debug.Log ("Chunk time on CPU: " + (Time.realtimeSinceStartup - cpuStartTime)); loadingChunk.CreateSeamChunk(thread.m_Root, thread.m_Root.min); if (loadingChunk.GenerateMesh(thread.m_Vertices, thread.m_Normals, thread.m_Indices) > 0) { loadingChunk.containsNothing = false; } List <Chunk> seamChunks = new List <Chunk>(); bool foundAllEight = FindSeamChunks(loadingChunk, seamChunks); if (foundAllEight) { List <OctreeNode> seams = FindSeamNodes(loadingChunk, seamChunks); OctreeNode seamRoot = loadingChunk.BuildSeamTree(seams, loadingChunk.min, octreeSize * 2); if (seamRoot != null) { Chunk seamChunk = new Chunk(); seamChunk.LOD = 6; seamChunk.CreateSeamChunk(seamRoot, seamRoot.min); seamChunk.GenerateMesh(); //Debug.Log (seamChunk.GenerateMesh()); seamChunk.DestroyOctree(); loadingChunk.seamChunk = seamChunk; } } //loadingChunk.DestroyOctree(); } if (loadingChunk != null && thread.m_Root == null) { loadingChunk.DestroyMesh(); } if (loadingChunk != null) { chunks.Add(loadingChunk.min, loadingChunk); } } else { if (thread.m_Root != null && changingChunk != null) { changingChunk.clearMesh(); changingChunk.DestroyOctree(); changingChunk.CreateSeamChunk(thread.m_Root, thread.m_Root.min); //float meshReloadStart = Time.realtimeSinceStartup; changingChunk.GenerateMesh(thread.m_Vertices, thread.m_Normals, thread.m_Indices); //Debug.Log (Time.realtimeSinceStartup - meshReloadStart); List <Chunk> seamChunks = new List <Chunk>(); bool foundAllEight = FindSeamChunks(changingChunk, seamChunks); if (foundAllEight) { List <OctreeNode> seams = FindSeamNodes(changingChunk, seamChunks); OctreeNode seamRoot = changingChunk.BuildSeamTree(seams, changingChunk.min, octreeSize * 2); if (seamRoot != null) { Chunk seamChunk = changingChunk.seamChunk; if (seamChunk == null) { seamChunk = new Chunk(); changingChunk.seamChunk = seamChunk; } if (seamChunk != null) { seamChunk.LOD = 6; seamChunk.CreateSeamChunk(seamRoot, seamRoot.min); seamChunk.GenerateMesh(); //Debug.Log (seamChunk.GenerateMesh()); seamChunk.DestroyOctree(); } } } //changingChunk.DestroyOctree(); changingChunk = null; } } thread.m_Vertices.Clear(); thread.m_Normals.Clear(); thread.m_Indices.Clear(); loadingChunk = null; thread.m_Root = null; } }
private OctreeNode <T> CreateNewNode(ref BoundingBox bounds) { OctreeNode <T> node = new OctreeNode <T>(ref bounds, MaxChildren, this, null); return(node); }
public void ContourFaceProc(OctreeNode[] node, int dir, MeshData data) { if (node[0] == null || node[1] == null) { return; } if (node[0].type == NodeType.INTERNAL || node[1].type == NodeType.INTERNAL) { for (int i = 0; i < 4; i++) { OctreeNode[] faceNodes = new OctreeNode[2]; int[] c = { faceProcFaceMask[dir][i][0], faceProcFaceMask[dir][i][1], }; for (int j = 0; j < 2; j++) { if (node[j].type != NodeType.INTERNAL) { faceNodes[j] = node[j]; } else { faceNodes[j] = node[j].children[c[j]]; } } ContourFaceProc(faceNodes, faceProcFaceMask[dir][i][2], data); } int[][] orders = { new int[] { 0, 0, 1, 1 }, new int[] { 0, 1, 0, 1 }, }; for (int i = 0; i < 4; i++) { OctreeNode[] edgeNodes = new OctreeNode[4]; int[] c = { faceProcEdgeMask[dir][i][1], faceProcEdgeMask[dir][i][2], faceProcEdgeMask[dir][i][3], faceProcEdgeMask[dir][i][4], }; int[] order = orders[faceProcEdgeMask[dir][i][0]]; for (int j = 0; j < 4; j++) { if (node[order[j]].type == NodeType.LEAF || node[order[j]].type == NodeType.PSEUDO) { edgeNodes[j] = node[order[j]]; } else { edgeNodes[j] = node[order[j]].children[c[j]]; } } ContourEdgeProc(edgeNodes, faceProcEdgeMask[dir][i][5], data); } } }
//semplifica l'octree public OctreeNode SimplifyOctree(OctreeNode node, float threshold) { if (node == null) { return(null); } if (node.type != NodeType.INTERNAL) { // can't simplify! return(node); } QefSolver qef = new QefSolver(); int[] signs = new int[8] { -1, -1, -1, -1, -1, -1, -1, -1, }; int midsign = -1; int edgeCount = 0; bool isCollapsible = true; for (int i = 0; i < 8; i++) { node.children[i] = SimplifyOctree(node.children[i], threshold); if (node.children[i] != null) { OctreeNode child = node.children[i]; if (child.type == NodeType.INTERNAL) { isCollapsible = false; } else { qef.Add(child.nodeInfo.qef); midsign = (child.nodeInfo.corners >> (7 - i)) & 1; signs[i] = (child.nodeInfo.corners >> i) & 1; edgeCount++; } } } if (!isCollapsible) { // at least one child is an internal node, can't collapse return(node); } Vector3 qefPosition = new Vector3(); float error = qef.Solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); Vector3 position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z); // at this point the masspoint will actually be a sum, so divide to make it the average if (error > threshold) { // this collapse breaches the threshold return(node); } if (position.x < node.min.x || position.x > (node.min.x + node.size) || position.y < node.min.y || position.y > (node.min.y + node.size) || position.z < node.min.z || position.z > (node.min.z + node.size)) { position = qef.GetMassPoint(); } // change the node from an internal node to a 'pseudo leaf' node NodeInfo info = new NodeInfo(); for (int i = 0; i < 8; i++) { if (signs[i] == -1) { // Undetermined, use centre sign instead info.corners |= (midsign << i); } else { info.corners |= (signs[i] << i); } } info.avgNormal = new Vector3(0, 0, 0); for (int i = 0; i < 8; i++) { if (node.children[i] != null) { if (node.children[i].type == NodeType.PSEUDO || node.children[i].type == NodeType.LEAF) { info.avgNormal += node.children[i].nodeInfo.avgNormal; } } } info.avgNormal = info.avgNormal.normalized; info.position = position; info.qef = qef.GetData(); for (int i = 0; i < 8; i++) { DestroyOctree(node.children[i]); node.children[i] = null; } node.type = NodeType.PSEUDO; node.nodeInfo.Set(info); return(node); }
/// <summary> /// Add a color into the tree /// </summary> /// <param name="pixel">The color</param> /// <param name="colorBits">The number of significant color bits</param> /// <param name="level">The level in the tree</param> /// <param name="octree">The tree to which this node belongs</param> public void addColor(Color32* pixel, int colorBits, int level, Octree octree) { // Update the color information if this is a leaf if (_leaf) { inc(pixel); // Setup the previous node octree.trackPrevious(this); } else { // Go to the next level down in the tree int shift = 7 - level; int index = ((pixel->Red & mask[level]) >> (shift - 2)) | ((pixel->Green & mask[level]) >> (shift - 1)) | ((pixel->Blue & mask[level]) >> (shift)); OctreeNode child = _children[index]; if (child == null) { // Create a new child node & store in the array child = new OctreeNode(level + 1, colorBits, octree); _children[index] = child; } // Add the color to the child node child.addColor(pixel, colorBits, level + 1, octree); } }
/// <summary> /// Create an octree around the given root node. /// </summary> /// <param name="root"></param> private Octree(OctreeNode <StoredType> root) { depth = root.depth; this.root = root; }
private OctreeNode_GPU OctreeNode2OctreeNode_GPU(OctreeNode node) { return(new OctreeNode_GPU(node)); }
/// <summary>When a reducible node is added, this method is called to add it to the appropriate /// reducible node list (given its level)</summary> /// <param name="reducibleNode">node to add to a reducible list</param> private void AddReducibleNode(OctreeNode reducibleNode) { // hook this node into the front of the list. // this means the last one added will be the first in the list. reducibleNode.NextReducibleNode = this.octreeReducibleNodes[reducibleNode.Level]; this.octreeReducibleNodes[reducibleNode.Level] = reducibleNode; }
public OctreeNode(T obj, OctreeNode <T> parent) { mObject = obj; mParentNode = parent; mChilden = new OctreeNode <T> [(int)OctreeNodePos.max]; }
private void ContourFaceProc(OctreeNode[] node, int dir, List <int> indices) { if (node[0] == null || node[1] == null) { return; } if (node[0].type == OctreeNodeType.Node_Internal || node[1].type == OctreeNodeType.Node_Internal) { for (int i = 0; i < 4; i++) { OctreeNode[] faceNodes = new OctreeNode[2]; int[] c = new int[2] { faceProcFaceMask[dir, i, 0], faceProcFaceMask[dir, i, 1] }; for (int j = 0; j < 2; j++) { if (node[j].type != OctreeNodeType.Node_Internal) { faceNodes[j] = node[j]; } else { faceNodes[j] = node[j].children[c[j]]; } } ContourFaceProc(faceNodes, faceProcFaceMask[dir, i, 2], indices); } int[,] orders = new int[, ] { { 0, 0, 1, 1 }, { 0, 1, 0, 1 } }; for (int i = 0; i < 4; i++) { OctreeNode[] edgeNodes = new OctreeNode[4]; int[] c = new int[4] { faceProcEdgeMask[dir, i, 1], faceProcEdgeMask[dir, i, 2], faceProcEdgeMask[dir, i, 3], faceProcEdgeMask[dir, i, 4] }; int[] order = new int[4] { orders[faceProcEdgeMask[dir, i, 0], 0], orders[faceProcEdgeMask[dir, i, 0], 1], orders[faceProcEdgeMask[dir, i, 0], 2], orders[faceProcEdgeMask[dir, i, 0], 3] }; for (int j = 0; j < 4; j++) { if (node[order[j]].type == OctreeNodeType.Node_Leaf || node[order[j]].type == OctreeNodeType.Node_Psuedo) { edgeNodes[j] = node[order[j]]; } else { edgeNodes[j] = node[order[j]].children[c[j]]; } } ContourEdgeProc(edgeNodes, faceProcEdgeMask[dir, i, 5], indices); } } }
private int childHasObjects = 0; //Bitmask for child nodes public void Initialize(int index, LooseOctree <T> tree, OctreeNode parent) { Initialize(index, tree); this.parent = parent; treeDepth = parent.treeDepth + 1; }
/// /// <summary> /// Recursively traverse the octree looking for the closest object intersected by the rage /// </summary> /// /// <param name="node"> /// The node to traverse from /// </param> /// /// <param name="ray"> /// The ray to check for intersections against /// </param> /// /// <param name="intersectedPosition"> /// [output] the postion of the closest object intersected /// </param> /// /// <returns> /// The distance to the nearest object intersected by the input ray (null if there are no intersections) /// </returns> /// private float?RayIntersection(OctreeNode node, Ray ray, out Vector3 intersectedPosition) { intersectedPosition = new Vector3(); BoundingBox translatedBoundingBox = _masterBoundingBoxList[node.BoundingBoxIndex]; translatedBoundingBox.Max += node.BoundingBoxPosition; translatedBoundingBox.Min += node.BoundingBoxPosition; if (ray.Intersects(translatedBoundingBox) != null) { if (node.Children == null) { float shortestDistance = float.MaxValue; int shortestDistanceIndex = -1; for (int i = 0; i < node.InternalObjectPositions.Count; ++i) { float?distance = ray.Intersects(node.InternalObjectBoundingBoxes[i]); if (distance.HasValue && distance.Value < shortestDistance) { shortestDistance = distance.Value; shortestDistanceIndex = i; } } if (shortestDistance == float.MaxValue) { return(null); } else { intersectedPosition = node.InternalObjectPositions[shortestDistanceIndex]; return(shortestDistance); } } else { float shortestDistance = float.MaxValue; foreach (OctreeNode childNode in node.Children) { Vector3 position; float? distance = RayIntersection(childNode, ray, out position); if (distance.HasValue && distance.Value < shortestDistance) { shortestDistance = distance.Value; intersectedPosition = position; } } if (shortestDistance == float.MaxValue) { return(null); } else { return(shortestDistance); } } } return(null); }
public Octree(float xMax, float xMin, float yMax, float yMin, float zMax, float zMin, int maxItems, float minSize) { Top = new OctreeNode(xMax, xMin, yMax, yMin, zMax, zMin, maxItems, minSize); }
/// /// <summary> /// C'tor /// </summary> /// /// <param name="topLevelBoundingBox"> /// A top level bounding showing the size of the region that this octree will represent /// </param> /// /// <param name="topLevelBoundingBoxPosition"> /// The world coordinates of the minimum corner of the top level bounding box /// </param> /// public Octree(BoundingBox topLevelBoundingBox, Vector3 topLevelBoundingBoxPosition) { _masterBoundingBoxList.Add(topLevelBoundingBox); _root = new OctreeNode(_masterBoundingBoxList.Count - 1, topLevelBoundingBoxPosition); }
/// <summary> /// Construct the octree /// </summary> /// <param name="maxColorBits">The maximum number of significant bits in the image</param> public Octree(int maxColorBits) { _maxColorBits = maxColorBits; _leafCount = 0; _reducibleNodes = new OctreeNode[9]; _root = new OctreeNode(0, _maxColorBits, this); _previousColor = 0; _previousNode = null; }
public OctreeRenderer(OctreeNode <T> octree, AssetDatabase ad, RenderContext rc) : base(ad, rc, RgbaFloat.Red) { _octree = octree; }
/// <summary> /// Construct the node /// </summary> /// <param name="level">The level in the tree = 0 - 7</param> /// <param name="colorBits">The number of significant color bits in the image</param> /// <param name="octree">The tree to which this node belongs</param> public OctreeNode(int level, int colorBits, Octree octree) { // Construct the new node _leaf = (level == colorBits); _red = _green = _blue = 0; _pixelCount = 0; // If a leaf, increment the leaf count if (_leaf) { octree.Leaves++; _nextReducible = null; _children = null; } else { // Otherwise add this to the reducible nodes _nextReducible = octree.ReducibleNodes[level]; octree.ReducibleNodes[level] = this; _children = new OctreeNode[8]; } }
/// <summary> /// Recursively updates tree visibility /// </summary> /// <param name="n">Current node</param> /// <param name="p">Camera frustum planes</param> private void __updateOctreeNodeVisibility(OctreeNode n, Plane[] p) { if (GeometryUtility.TestPlanesAABB(p, n.AABB) && CanCameraSeeAABB(n.AABB, Camera.main.transform.position)) { n.gameObject.SetActive(true); for (int i = 0; i < n.children.Count; i++) __updateOctreeNodeVisibility(n.children[i], p); } else n.gameObject.SetActive(false); }
/// <summary> /// Keep track of the previous node that was quantized /// </summary> /// <param name="node"> /// The node last quantized /// </param> protected void TrackPrevious(OctreeNode node) { this.previousNode = node; }
/// <summary> /// Keep track of the previous node that was quantized /// </summary> /// <param name="node">The node last quantized</param> protected void trackPrevious(OctreeNode node) { _previousNode = node; }
/// <summary> /// Initializes a new instance of the <see cref="OctreeNode"/> class. /// </summary> /// <param name="level"> /// The level in the tree = 0 - 7 /// </param> /// <param name="colorBits"> /// The number of significant color bits in the image /// </param> /// <param name="octree"> /// The tree to which this node belongs /// </param> public OctreeNode(int level, int colorBits, Octree octree) { // Construct the new node this.leaf = level == colorBits; this.red = this.green = this.blue = 0; this.pixelCount = 0; // If a leaf, increment the leaf count if (this.leaf) { octree.Leaves++; this.nextReducible = null; this.children = null; } else { // Otherwise add this to the reducible nodes this.nextReducible = octree.ReducibleNodes[level]; octree.ReducibleNodes[level] = this; this.children = new OctreeNode[8]; } }
/// <summary> /// Recursively removes empty nodes /// </summary> /// <param name="n">Current node</param> /// <param name="_deepestNodes">List of deepest nodes to remove values from</param> private void __removeEmptyTreeNodes(OctreeNode n, List<OctreeNode> _deepestNodes) { for (int i = n.children.Count - 1; i >= 0; i--) __removeEmptyTreeNodes(n.children[i], _deepestNodes); if (n.children.Count == 0 && (n.value == null || n.value.mapObjects.Count == 0)) { _deepestNodes.Remove(n); GameObject.Destroy(n.gameObject); n.parent.children.Remove(n); } }
/// <summary>Initializes a new instance of the <see cref="Octree"/> class. Constructor</summary> /// <param name="pixelFormat">desired pixel format</param> internal Octree(PixelFormat pixelFormat) { // figure out the maximum colors from the pixel format passed in switch (pixelFormat) { case PixelFormat.Format1bppIndexed: this.octreeMaxColors = 2; break; case PixelFormat.Format4bppIndexed: this.octreeMaxColors = 16; break; case PixelFormat.Format8bppIndexed: this.octreeMaxColors = 256; break; default: throw new ArgumentException("Invalid Pixel Format", "pixelFormat"); } // we need a list for each level that may have reducible nodes. // since the last level (level 7) is only made up of leaf nodes, // we don't need an array entry for it. this.octreeReducibleNodes = new OctreeNode[7]; // add the initial level-0 root node this.octreeRoot = new OctreeNode(0, this); }
/// <summary> /// Find a node that fully contains the given bounds /// </summary> public bool ContainingNode(ref Vector3 center, ref Vector3 minBounds, ref Vector3 maxBounds, out OctreeNode node) { node = null; OctreeNode childNode = this;//Start here int index; while (true) { if (!childNode.ContainsBounds(ref minBounds, ref maxBounds)) { //Doesn't fit in this node. Put it in the parent node. if (childNode.childIndex == -1) { return(false); } node = childNode.parent; return(true); } index = childNode.BestFitChild(ref center); if (!childNode.isLeaf) { //Drop down another level if we have children childNode = childNode.children[index]; } else { //Place it here. No children node = childNode; return(true); } } }
/// <summary>set up the values we need to reuse the given pointer if the next color is argb</summary> /// <param name="node">last node to which we added a color</param> /// <param name="argb">last color we added</param> private void SetLastNode(OctreeNode node, int argb) { this.octreeLastNode = node; this.octreeLastArgb = argb; }
/// <summary> Keep track of the previous node that was quantized. </summary> /// <param name="node"> The node last quantized. </param> void TrackPrevious( OctreeNode node ) { previousNode = node; }
/// <summary> /// 分八个区 /// </summary> /// <param name="childNodes">八个子节点</param> /// <param name="oneBounds">当前节点的信息</param> private void SplitBounds(ref OctreeNode <T> currentNode, List <GameObject> objectList, int deep) { if (deep < m_MaxDepth) { currentNode.m_ObjectList = new List <GameObject>(); // 1、判断是否相交 相交就添加到当前节点的列表里 foreach (var gameObject in objectList) { if (gameObject.GetComponent <SphereCollider>() != null) { var gameObjectBounds = gameObject.GetComponent <Collider>().bounds; // 如果gameobject与分区相交 就添加到当前分区的列表里 if (currentNode.m_Bounds.Intersects(gameObjectBounds)) { currentNode.m_ObjectList.Add(gameObject); } } } // 2、分八个区域 currentNode.m_ChildNodes = new OctreeNode <T> [8]; currentNode.m_CurrentDepth = deep; var oneBounds = currentNode.m_Bounds; Vector3 half2Vector = oneBounds.size / 4; // 八块空间的位置 currentNode.m_ChildNodes[0].m_Bounds.center = oneBounds.center - half2Vector; currentNode.m_ChildNodes[0].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[1].m_Bounds.center = currentNode.m_ChildNodes[0].m_Bounds.center + new Vector3(oneBounds.size.x / 2, 0, 0); currentNode.m_ChildNodes[1].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[2].m_Bounds.center = currentNode.m_ChildNodes[1].m_Bounds.center + new Vector3(0, 0, oneBounds.size.z / 2); currentNode.m_ChildNodes[2].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[3].m_Bounds.center = currentNode.m_ChildNodes[0].m_Bounds.center + new Vector3(0, 0, oneBounds.size.z / 2); currentNode.m_ChildNodes[3].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[4].m_Bounds.center = currentNode.m_ChildNodes[0].m_Bounds.center + new Vector3(0, oneBounds.size.y / 2, 0); currentNode.m_ChildNodes[4].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[5].m_Bounds.center = currentNode.m_ChildNodes[1].m_Bounds.center + new Vector3(0, oneBounds.size.y / 2, 0); currentNode.m_ChildNodes[5].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[6].m_Bounds.center = currentNode.m_ChildNodes[2].m_Bounds.center + new Vector3(0, oneBounds.size.y / 2, 0); currentNode.m_ChildNodes[6].m_Bounds.size = oneBounds.size / 2; currentNode.m_ChildNodes[7].m_Bounds.center = currentNode.m_ChildNodes[3].m_Bounds.center + new Vector3(0, oneBounds.size.y / 2, 0); currentNode.m_ChildNodes[7].m_Bounds.size = oneBounds.size / 2; // 3、遍历8个区域 递归调用 for (int i = 0; i < currentNode.m_ChildNodes.Length; i++) { // 递归调用 if (currentNode.m_ObjectList.Count != 0) { // check if child node intersect bool isIntersects = false; currentNode.m_ChildNodes[i].m_ObjectList = new List <GameObject>(); foreach (var gameObject in currentNode.m_ObjectList) { if (gameObject.GetComponent <SphereCollider>() != null) { var gameObjectBounds = gameObject.GetComponent <Collider>().bounds; if (currentNode.m_ChildNodes[i].m_Bounds.Intersects(gameObjectBounds)) { currentNode.m_ChildNodes[i].m_ObjectList.Add(gameObject); isIntersects = true; } } } if (isIntersects) { if (currentNode.m_CurrentDepth != 0 && currentNode.m_ObjectList.Count == currentNode.m_ChildNodes[i].m_ObjectList.Count) { continue; } currentNode.m_CurrentDepth++; SplitBounds(ref currentNode.m_ChildNodes[i], currentNode.m_ObjectList, currentNode.m_CurrentDepth); } } } } }
/// <summary> /// Reduce the depth of the tree /// </summary> public void Reduce() { int index; // Find the deepest level containing at least one reducible node for (index = _maxColorBits - 1; (index > 0) && (null == _reducibleNodes[index]); index--) ; // Reduce the node most recently added to the list at level 'index' OctreeNode node = _reducibleNodes[index]; _reducibleNodes[index] = node.NextReducible; // Decrement the leaf count after reducing the node _leafCount -= node.Reduce(); // And just in case I've reduced the last color to be added, and the next color to // be added is the same, invalidate the previousNode... _previousNode = null; }
public void Add(T content) { if (!this.Bounds.Contains(content.Position)) { throw new UnityException("Cannot add an object outside of octree"); } // just add if empty if ((IsLeaf() && _Contents.Count == 0) || Level == MaxLevel) { _Contents.Add(content); } else { if (content.Position.Equals(this.Bounds.center) || (_Contents.Count > 0 && _Contents.TrueForAll(i => content.Position.Equals(i.Position)))) { _Contents.Add(content); } else { if (IsLeaf()) { // build children _IsLeaf = false; var ex = this.Bounds.extents * 0.5f; Children[0] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(-ex.x, -ex.y, -ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[1] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(-ex.x, -ex.y, ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[2] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(ex.x, -ex.y, -ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[3] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(ex.x, -ex.y, ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[4] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(-ex.x, ex.y, -ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[5] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(-ex.x, ex.y, ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[6] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(ex.x, ex.y, -ex.z), this.Bounds.extents), Level + 1, MaxLevel); Children[7] = new OctreeNode <T>(new Bounds(this.Bounds.center + new Vector3(ex.x, ex.y, ex.z), this.Bounds.extents), Level + 1, MaxLevel); // move any existing items for (var i = 0; i < Contents.Count; i++) { if (!content.Position.Equals(this.Bounds.center)) { for (var j = 0; j < 8; j++) { T c = Contents[i]; if (Children[j].Contains(c.Position)) { Children[j].Add(c); } } _Contents.Clear(); } } } // add new item into child for (var i = 0; i < 8; i++) { if (Children[i].Contains(content.Position)) { Children[i].Add(content); } } } } }
/// <summary> /// Initializes a new instance of the <see cref="Octree"/> class. /// </summary> /// <param name="maxColorBits"> /// The maximum number of significant bits in the image /// </param> public Octree(int maxColorBits) { this.maxColorBits = maxColorBits; this.leafCount = 0; this.reducibleNodes = new OctreeNode[9]; this.root = new OctreeNode(0, this.maxColorBits, this); this.previousColor = 0; this.previousNode = null; }
private void AddVerticesAndIndices(OctreeNode <T> octree, List <VertexPositionNormalTexture> vertices, List <int> indices) { int baseIndex = vertices.Count; var bounds = octree.Bounds; vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Min.X, bounds.Min.Y, bounds.Min.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Min.X, bounds.Max.Y, bounds.Min.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Max.X, bounds.Max.Y, bounds.Min.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Max.X, bounds.Min.Y, bounds.Min.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Min.X, bounds.Min.Y, bounds.Max.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Min.X, bounds.Max.Y, bounds.Max.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Max.X, bounds.Max.Y, bounds.Max.Z), Vector3.Zero, Vector2.Zero)); vertices.Add(new VertexPositionNormalTexture(new Vector3(bounds.Max.X, bounds.Min.Y, bounds.Max.Z), Vector3.Zero, Vector2.Zero)); indices.Add(baseIndex + 0); indices.Add(baseIndex + 1); indices.Add(baseIndex + 0); indices.Add(baseIndex + 1); indices.Add(baseIndex + 2); indices.Add(baseIndex + 1); indices.Add(baseIndex + 2); indices.Add(baseIndex + 3); indices.Add(baseIndex + 2); indices.Add(baseIndex + 0); indices.Add(baseIndex + 3); indices.Add(baseIndex + 0); indices.Add(baseIndex + 4); indices.Add(baseIndex + 5); indices.Add(baseIndex + 4); indices.Add(baseIndex + 5); indices.Add(baseIndex + 1); indices.Add(baseIndex + 5); indices.Add(baseIndex + 1); indices.Add(baseIndex + 0); indices.Add(baseIndex + 1); indices.Add(baseIndex + 4); indices.Add(baseIndex + 0); indices.Add(baseIndex + 4); indices.Add(baseIndex + 7); indices.Add(baseIndex + 6); indices.Add(baseIndex + 7); indices.Add(baseIndex + 6); indices.Add(baseIndex + 5); indices.Add(baseIndex + 6); indices.Add(baseIndex + 5); indices.Add(baseIndex + 4); indices.Add(baseIndex + 5); indices.Add(baseIndex + 7); indices.Add(baseIndex + 4); indices.Add(baseIndex + 7); indices.Add(baseIndex + 3); indices.Add(baseIndex + 2); indices.Add(baseIndex + 3); indices.Add(baseIndex + 2); indices.Add(baseIndex + 6); indices.Add(baseIndex + 2); indices.Add(baseIndex + 6); indices.Add(baseIndex + 7); indices.Add(baseIndex + 6); indices.Add(baseIndex + 3); indices.Add(baseIndex + 7); indices.Add(baseIndex + 3); foreach (var child in octree.Children) { AddVerticesAndIndices(child, vertices, indices); } }
/// <summary> /// Reduce the depth of the tree /// </summary> private void Reduce() { // Find the deepest level containing at least one reducible node int index = this.maxColorBits - 1; while ((index > 0) && (null == this.reducibleNodes[index])) { index--; } // Reduce the node most recently added to the list at level 'index' OctreeNode node = this.reducibleNodes[index]; this.reducibleNodes[index] = node.NextReducible; // Decrement the leaf count after reducing the node this.leafCount -= node.Reduce(); // And just in case I've reduced the last color to be added, and the next color to // be added is the same, invalidate the previousNode... this.previousNode = null; }
public Octree(Vector3 position, float size, int depth) { node = new OctreeNode <T>(position, size); node.Subdivide(depth); }
/// <summary> /// Add a color into the tree /// </summary> /// <param name="pixel"> /// The color /// </param> /// <param name="colorBits"> /// The number of significant color bits /// </param> /// <param name="level"> /// The level in the tree /// </param> /// <param name="octree"> /// The tree to which this node belongs /// </param> public void AddColor(Color32* pixel, int colorBits, int level, Octree octree) { // Update the color information if this is a leaf if (this.leaf) { this.Increment(pixel); // Setup the previous node octree.TrackPrevious(this); } else { // Go to the next level down in the tree int shift = 7 - level; int index = ((pixel->R & Mask[level]) >> (shift - 2)) | ((pixel->G & Mask[level]) >> (shift - 1)) | ((pixel->B & Mask[level]) >> shift); OctreeNode child = this.children[index]; if (null == child) { // Create a new child node & store in the array child = new OctreeNode(level + 1, colorBits, octree); this.children[index] = child; } // Add the color to the child node child.AddColor(pixel, colorBits, level + 1, octree); } }
//------------------------------------------------------------------------------------------- void UpdateOctree() { playerNode = OctreeNode.getRoot.CreateSubdivisionsWithItem(maxOctreeGenerations, Player.position); //Invoke("UpdateGrpahics", 0.4f); //UpdateGrpahics(); }
/// <summary> /// Return a color palette for the computed octree. /// </summary> /// <returns>A color palette for the computed octree</returns> internal Color[] GetPaletteColors() { // If we haven't already computed it, compute it now if (this.octreePalette == null) { // Start at the second-to-last reducible level int reductionLevel = this.octreeReducibleNodes.Length - 1; // We want to subtract one from the target if we have a transparent // bit because we want to save room for that special color int targetCount = this.octreeMaxColors - (this.octreeHasTransparent ? 1 : 0); // While we still have more colors than the target... while (this.octreeColorCount > targetCount) { // Find the first reducible node, starting with the last level // that can have reducible nodes while (reductionLevel > 0 && this.octreeReducibleNodes[reductionLevel] == null) { --reductionLevel; } if (this.octreeReducibleNodes[reductionLevel] == null) { // Shouldn't get here break; } // We should have a node now OctreeNode newLeaf = this.octreeReducibleNodes[reductionLevel]; this.octreeReducibleNodes[reductionLevel] = newLeaf.NextReducibleNode; this.octreeColorCount -= newLeaf.Reduce() - 1; } if (reductionLevel == 0 && !this.octreeHasTransparent) { // If this was the top-most level, we now only have a single color // representing the average. That's not what we want. // use just black and white this.octreePalette = new Color[2]; this.octreePalette[0] = Color.Black; this.octreePalette[1] = Color.White; // And empty the octree so it always picks the closer of the black and white entries this.octreeRoot = new OctreeNode(0, this); } else { // Now walk the tree, adding all the remaining colors to the list int paletteIndex = 0; this.octreePalette = new Color[this.octreeColorCount + (this.octreeHasTransparent ? 1 : 0)]; // Add the transparent color if we need it if (this.octreeHasTransparent) { this.octreePalette[paletteIndex++] = Color.Transparent; } // Have the nodes insert their leaf colors this.octreeRoot.AddColorsToPalette(this.octreePalette, ref paletteIndex); } } return this.octreePalette; }
private void Awake() { _vm = this; vdm = new DataGenerator(worldSize.y); OctreeNode.Init(); }
public OctreeNode ConstructUpwards(List <OctreeNode> inputNodes, Vector3 rootMin, int rootNodeSize) { if (inputNodes.Count == 0) { return(null); } int baseNodeSize = inputNodes[0].size; List <OctreeNode> nodes = new List <OctreeNode>(); for (int i = 0; i < inputNodes.Count; i++) { nodes.Add(inputNodes[i]); } nodes.Sort(delegate(OctreeNode lhs, OctreeNode rhs) { return(lhs.size - rhs.size); }); // the input nodes may be different sizes if a seam octree is being constructed // in that case we need to process the input nodes in stages along with the newly // constructed parent nodes until all the nodes have the same size while (nodes[0].size != nodes[nodes.Count - 1].size) { // find the end of this run int iter = 0; int size = nodes[iter].size; do { ++iter; } while (nodes[iter].size == size); // construct the new parent nodes for this run List <OctreeNode> newNodes = new List <OctreeNode>(); for (int i = 0; i < iter; i++) { newNodes.Add(nodes[i]); } newNodes = ConstructParents(newNodes, rootMin, size * 2); // set up for the next iteration: the parents produced plus any remaining input nodes for (int i = iter; i < nodes.Count; i++) { newNodes.Add(nodes[i]); } nodes.Clear(); for (int i = 0; i < newNodes.Count; i++) { nodes.Add(newNodes[i]); } newNodes.Clear(); newNodes = null; } int parentSize = nodes[0].size * 2; while (parentSize <= rootNodeSize) { nodes = ConstructParents(nodes, rootMin, parentSize); parentSize *= 2; } if (nodes.Count != 1) { Debug.Log(baseNodeSize); Debug.Log(rootMin); Debug.Log(rootNodeSize); Debug.LogError("There can only be one root node!"); Application.Quit(); } OctreeNode root = nodes[0]; nodes.Clear(); nodes = null; return(root); }