public Node(Node parent, Vector3 position, int subNodeID, int LOD, RenderType renderType) { densityChangeData = new DensityData(); this.parent = parent; this.position = position; this.subNodeID = subNodeID; this.LOD = LOD; float chunkWidth = (NodeManager.LODSize[LOD] * NodeManager.nodeSize) / 2f; center = new Vector3(position.x + chunkWidth, position.y + chunkWidth, position.z + chunkWidth); setRenderType(renderType); if (parent != null && parent.permanent) permanent = true; NodeManager.nodeCount[LOD]++; float nWidth = NodeManager.LODSize[LOD] * NodeManager.nodeSize; chunkPos.x = (int)(center.x / nWidth); chunkPos.y = (int)(center.y / nWidth); chunkPos.z = (int)(center.z / nWidth); if (LOD == 0) { string dir = getDirectory(); if (Directory.Exists(dir) && File.Exists(dir + "\\densities.txt")) MeshFactory.requestLoad(this); } regenReq = true; MeshFactory.requestMesh(this); }
/// <summary> /// Adds a node to the save queue. /// </summary> /// <param name="node"></param> public static void requestSave(Node node) { fileThread.enqueueSave(node); }
/// <summary> /// Adds a request to generate a mesh /// </summary> /// <param name="_node">The node the mesh is for</param> public static void requestMesh(Node _node) { MeshRequest req = new MeshRequest { node = _node, pos = _node.position, LOD = _node.LOD, isDone = false, hasDensities = (_node.densityData != null) }; if (!req.hasDensities) lock (requestArray[req.LOD + 1]) requestArray[req.LOD + 1].Enqueue(req); else lock (requestArray[0]) requestArray[0].Enqueue(req); }
/// <summary> /// Adds a node to the save queue. /// </summary> /// <param name="node"></param> public static void requestLoad(Node node) { fileThread.enqueueLoad(node); }
/// <summary> /// Queues the node for saving. /// </summary> /// <param name="node"></param> public void enqueueSave(Node node) { lock (saveQueue) if (!saveQueue.Contains(node)) saveQueue.Enqueue(node); }
/// <summary> /// Queues the node for loading. /// </summary> /// <param name="node"></param> public void enqueueLoad(Node node) { lock (loadQueue) loadQueue.Enqueue(node); }
/// <summary> /// Initializes the node manager. /// </summary> public static void init(string worldName) { NodeManager.worldName = worldName; float nSize = LODSize[maxLOD] * nodeSize; for (int x = -1; x < 2; x++) { for (int y = -1; y < 2; y++) { for (int z = -1; z < 2; z++) { topNodes[x + 1, y + 1, z + 1] = new Node(null, new Vector3(x * nSize, y * nSize, z * nSize), 0, maxLOD, Node.RenderType.FRONT); } } } viewChunkPos = new Vector3I[maxLOD + 1]; for (int i = 0; i <= maxLOD; i++) viewChunkPos[i] = new Vector3I(); }
/// <summary> /// Sets the view position, and checks if chunks need to be updated /// </summary> /// <param name="pos"></param> public static void setViewPosition(Vector3 pos) { /*Debug.Log(pos+",cameraPos:"+QuixelEngine.getCameraPos()); 测试结果,这里的pos确实就是camera的位置 所以应该每次camera位置更新以后这里就会被call 然后计算出各个LOD下面camera的viewPosition(V3I形式) */ for (int i = 0; i <= maxLOD; i++) { float nWidth = LODSize[i] * nodeSize; viewChunkPos[i].x = (int)(pos.x / nWidth); viewChunkPos[i].y = (int)(pos.y / nWidth); viewChunkPos[i].z = (int)(pos.z / nWidth); } float sWidth = LODSize[0] * nodeSize * 0.5f; //不知是谁设置的,但是这里的LODSize[0]已经是4了,毕竟init的时候debug就是这样,所以这里sWidth是32 Vector3I newPos = new Vector3I((int)(pos.x / sWidth), (int)(pos.y / sWidth), (int)(pos.z / sWidth)); //getTopNode不管character怎么掉都一直是0,-1,0. //不过如果一开始很高的话(大于0?)这个时侯会是0,0,0 //然后curTopNode和getTopNode倒是一直都很一致 //这是因为下面的这一大长段内容,如果curTop和Top不一样的话就马上进行调整 //newPos是为了调整curBottomNode if (!curTopNode.Equals(getTopNode(pos))) { float nodeWidth = LODSize[maxLOD] * nodeSize; Vector3I diff = getTopNode(pos).Subtract(curTopNode); curTopNode = getTopNode(pos); while (diff.x > 0) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { topNodes[0, y, z].dispose(); topNodes[0, y, z] = topNodes[1, y, z]; topNodes[1, y, z] = topNodes[2, y, z]; topNodes[2, y, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + nodeWidth, (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.x--; } while (diff.x < 0) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { topNodes[2, y, z].dispose(); topNodes[2, y, z] = topNodes[1, y, z]; topNodes[1, y, z] = topNodes[0, y, z]; topNodes[0, y, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) - nodeWidth, (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.x++; } while (diff.y > 0) { for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { topNodes[x, 0, z].dispose(); topNodes[x, 0, z] = topNodes[x, 1, z]; topNodes[x, 1, z] = topNodes[x, 2, z]; topNodes[x, 2, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + nodeWidth, (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.y--; } while (diff.y < 0) { for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { topNodes[x, 2, z].dispose(); topNodes[x, 2, z] = topNodes[x, 1, z]; topNodes[x, 1, z] = topNodes[x, 0, z]; topNodes[x, 0, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) - nodeWidth, (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.y++; } while (diff.z > 0) { for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { topNodes[x, y, 0].dispose(); topNodes[x, y, 0] = topNodes[x, y, 1]; topNodes[x, y, 1] = topNodes[x, y, 2]; topNodes[x, y, 2] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + nodeWidth), 0, maxLOD, Node.RenderType.FRONT); } } diff.z--; } while (diff.z < 0) { for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { topNodes[x, y, 2].dispose(); topNodes[x, y, 2] = topNodes[x, y, 1]; topNodes[x, y, 1] = topNodes[x, y, 0]; topNodes[x, y, 0] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) - nodeWidth), 0, maxLOD, Node.RenderType.FRONT); } } diff.z++; } } if (curBottomNode.x != newPos.x || curBottomNode.y != newPos.y || curBottomNode.z != newPos.z) { Vector3 setPos = new Vector3(newPos.x * sWidth + (sWidth / 1f), newPos.y * sWidth + (sWidth / 1f), newPos.z * sWidth + (sWidth / 1f)); for (int x = 0; x < 3; x++){ for (int y = 0; y < 3; y++){ for (int z = 0; z < 3; z++) { topNodes[x, y, z].viewPosChanged(setPos); //Debug.Log(setPos+",newpos:"+newPos+",curbuttom:"+curBottomNode); /* (32.0, -96.0, 32.0),newpos:(0,-4,0),curbuttom:(0,-3,0)>>(0,-4,0) */ }}} curBottomNode = newPos; } }
/// <summary> /// Populates the subnode array /// </summary> public void createSubNodes(RenderType type) { if (LOD == 0) return; if (subNodes[0] != null) { for (int i = 0; i < 8; i++) { if (subNodes[i].renderType != type) subNodes[i].setRenderType(type); subNodes[i].disposed = false; } return; } for (int i = 0; i < 8; i++) { if (subNodes[i] == null) subNodes[i] = new Node(this, NodeManager.getOffsetPosition(this, i), i, LOD - 1, type); } }
/// <summary> /// Searches for a node containing the given point and LOD, creating it if none is found. /// </summary> /// <param name="pos"></param> /// <returns></returns> public void searchNodeCreate(Vector3 pos, int searchLOD, ref Node[] list) { if (containsDensityPoint(pos)) { if (LOD == searchLOD) { for (int i = 0; i < list.Length; i++) if (list[i] == null) { list[i] = this; return; } Debug.Log("A"); } else { if (isBottomLevel()) createSubNodes(RenderType.FRONT); for (int i = 0; i < 8; i++) { subNodes[i].searchNodeCreate(pos, searchLOD, ref list); } } } }
/// <summary> /// Sets the view position, and checks if chunks need to be updated /// </summary> /// <param name="pos"></param> public static void setViewPosition(Vector3 pos) { for (int i = 0; i <= maxLOD; i++) { float nWidth = LODSize[i] * nodeSize; viewChunkPos[i].x = (int)(pos.x / nWidth); viewChunkPos[i].y = (int)(pos.y / nWidth); viewChunkPos[i].z = (int)(pos.z / nWidth); } float sWidth = LODSize[0] * nodeSize * 0.5f; Vector3I newPos = new Vector3I((int)(pos.x / sWidth), (int)(pos.y / sWidth), (int)(pos.z / sWidth)); if (!curTopNode.Equals(getTopNode(pos))) { float nodeWidth = LODSize[maxLOD] * nodeSize; Vector3I diff = getTopNode(pos).Subtract(curTopNode); curTopNode = getTopNode(pos); while (diff.x > 0) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { topNodes[0, y, z].dispose(); topNodes[0, y, z] = topNodes[1, y, z]; topNodes[1, y, z] = topNodes[2, y, z]; topNodes[2, y, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + nodeWidth, (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.x--; } while (diff.x < 0) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { topNodes[2, y, z].dispose(); topNodes[2, y, z] = topNodes[1, y, z]; topNodes[1, y, z] = topNodes[0, y, z]; topNodes[0, y, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) - nodeWidth, (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.x++; } while (diff.y > 0) { for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { topNodes[x, 0, z].dispose(); topNodes[x, 0, z] = topNodes[x, 1, z]; topNodes[x, 1, z] = topNodes[x, 2, z]; topNodes[x, 2, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + nodeWidth, (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.y--; } while (diff.y < 0) { for (int x = 0; x < 3; x++) { for (int z = 0; z < 3; z++) { topNodes[x, 2, z].dispose(); topNodes[x, 2, z] = topNodes[x, 1, z]; topNodes[x, 1, z] = topNodes[x, 0, z]; topNodes[x, 0, z] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) - nodeWidth, (curTopNode.z * nodeWidth) + ((z - 1) * nodeWidth)), 0, maxLOD, Node.RenderType.FRONT); } } diff.y++; } while (diff.z > 0) { for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { topNodes[x, y, 0].dispose(); topNodes[x, y, 0] = topNodes[x, y, 1]; topNodes[x, y, 1] = topNodes[x, y, 2]; topNodes[x, y, 2] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) + nodeWidth), 0, maxLOD, Node.RenderType.FRONT); } } diff.z--; } while (diff.z < 0) { for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { topNodes[x, y, 2].dispose(); topNodes[x, y, 2] = topNodes[x, y, 1]; topNodes[x, y, 1] = topNodes[x, y, 0]; topNodes[x, y, 0] = new Node(null, new Vector3((curTopNode.x * nodeWidth) + ((x - 1) * nodeWidth), (curTopNode.y * nodeWidth) + ((y - 1) * nodeWidth), (curTopNode.z * nodeWidth) - nodeWidth), 0, maxLOD, Node.RenderType.FRONT); } } diff.z++; } } if (curBottomNode.x != newPos.x || curBottomNode.y != newPos.y || curBottomNode.z != newPos.z) { Vector3 setPos = new Vector3(newPos.x * sWidth + (sWidth / 1f), newPos.y * sWidth + (sWidth / 1f), newPos.z * sWidth + (sWidth / 1f)); for (int x = 0; x < 3; x++) for (int y = 0; y < 3; y++) for (int z = 0; z < 3; z++) { topNodes[x, y, z].viewPosChanged(setPos); } curBottomNode = newPos; } }
/// <summary> /// Returns a node containing the point as close as possible to the requested LOD. /// </summary> /// <param name="pos"></param> /// <param name="searchLOD"></param> /// <returns>Null if no such node exists.</returns> public static Node[] searchNodeContainingDensity(Vector3 pos, int searchLOD) { Node[] ret = new Node[8]; for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { if (topNodes[x, y, z] != null) topNodes[x, y, z].searchNodeCreate(pos, searchLOD, ref ret); } } } return ret; }
/// <summary> /// Calculates the offset position given a parent's node and the subnode ID. /// </summary> /// <param name="parentNode">Parent that contains t</param> /// <param name="subNodeID">Index of the node in the subnode array</param> /// <returns></returns> public static Vector3 getOffsetPosition(Node parentNode, int subNodeID) { //Vector3 ret = new Vector3(parentNode.position.x, parentNode.position.y, parentNode.position.z); //Debug.Log("getOffsetPosition triggered, parentNode is:"+parentNode+",subNodeID is "+subNodeID); //subNodeID 从0~7,而parentNode要具体看Node的代码先,现在看到有LOD和position的值 //subNodeID 的意义是正方体8个点? int parentWidth = nodeSize * LODSize[parentNode.LOD]; return new Vector3 { x = parentNode.position.x + ((parentWidth / 2) * mask[subNodeID].x), y = parentNode.position.y + ((parentWidth / 2) * mask[subNodeID].y), z = parentNode.position.z + ((parentWidth / 2) * mask[subNodeID].z) }; //这样子没问题,8个顶点可以提供全部的27个子点 }
/// <summary> /// Calculates a density value given a location /// </summary> /// <param name="node"></param> /// <param name="pos"></param> /// <returns></returns> private static VoxelData calculateDensity(Node node, Vector3I pos) { int nodeWidth = NodeManager.LODSize[node.LOD]; Vector3 ws = new Vector3(node.position.x + (nodeWidth * pos.x), node.position.y + (nodeWidth * pos.y), node.position.z + (nodeWidth * pos.z)); return terrainGenerator.calculateDensity(ws); }
/// <summary> /// Calculates the offset position given a parent's node and the subnode ID. /// </summary> /// <param name="parentNode">Parent that contains t</param> /// <param name="subNodeID">Index of the node in the subnode array</param> /// <returns></returns> public static Vector3 getOffsetPosition(Node parentNode, int subNodeID) { //Vector3 ret = new Vector3(parentNode.position.x, parentNode.position.y, parentNode.position.z); int parentWidth = nodeSize * LODSize[parentNode.LOD]; return new Vector3 { x = parentNode.position.x + ((parentWidth / 2) * mask[subNodeID].x), y = parentNode.position.y + ((parentWidth / 2) * mask[subNodeID].y), z = parentNode.position.z + ((parentWidth / 2) * mask[subNodeID].z) }; }
/// <summary> /// Generates the triangles for a specific voxel /// </summary> /// <param name="node">The node that contains the voxel</param> /// <param name="pos">The voxel position (Not real position) [16,16,16]</param> /// <param name="triangleList">The list used to contain triangles made so far</param> /// <param name="densities">The array that contains density information</param> /// <param name="densityNormals">The array that contains density normals</param> private static void generateTriangles(Node node, Vector3I pos, List<Triangle> triangleList, List<int> submeshIDList, int[] subMeshTriCount, DensityData densities, Vector3[, ,] densityNormals) { float size = NodeManager.LODSize[node.LOD]; float[] denses = new float[8]; denses[0] = densities.get(pos.x, pos.y, pos.z + 1); denses[1] = densities.get(pos.x + 1, pos.y, pos.z + 1); denses[2] = densities.get(pos.x + 1, pos.y, pos.z); denses[3] = densities.get(pos.x, pos.y, pos.z); denses[4] = densities.get(pos.x, pos.y + 1, pos.z + 1); denses[5] = densities.get(pos.x + 1, pos.y + 1, pos.z + 1); denses[6] = densities.get(pos.x + 1, pos.y + 1, pos.z); denses[7] = densities.get(pos.x, pos.y + 1, pos.z); byte cubeIndex = 0; if (denses[0] < isolevel) cubeIndex |= 1; if (denses[1] < isolevel) cubeIndex |= 2; if (denses[2] < isolevel) cubeIndex |= 4; if (denses[3] < isolevel) cubeIndex |= 8; if (denses[4] < isolevel) cubeIndex |= 16; if (denses[5] < isolevel) cubeIndex |= 32; if (denses[6] < isolevel) cubeIndex |= 64; if (denses[7] < isolevel) cubeIndex |= 128; if (cubeIndex == 0 || cubeIndex == 255) return; Vector3 origin = new Vector3((size * (pos.x)) , (size * (pos.y)) , (size * (pos.z))); Vector3[] positions = new Vector3[8]; positions[0] = new Vector3(origin.x, origin.y, origin.z + size); positions[1] = new Vector3(origin.x + size, origin.y, origin.z + size); positions[2] = new Vector3(origin.x + size, origin.y, origin.z); positions[3] = new Vector3(origin.x, origin.y, origin.z); positions[4] = new Vector3(origin.x, origin.y + size, origin.z + size); positions[5] = new Vector3(origin.x + size, origin.y + size, origin.z + size); positions[6] = new Vector3(origin.x + size, origin.y + size, origin.z); positions[7] = new Vector3(origin.x, origin.y + size, origin.z); Vector3[][] vertlist = new Vector3[12][]; if (IsBitSet(edgeTable[cubeIndex], 1)) vertlist[0] = VertexInterp(isolevel, positions[0], positions[1], denses[0], denses[1], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 2)) vertlist[1] = VertexInterp(isolevel, positions[1], positions[2], denses[1], denses[2], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 4)) vertlist[2] = VertexInterp(isolevel, positions[2], positions[3], denses[2], denses[3], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 8)) vertlist[3] = VertexInterp(isolevel, positions[3], positions[0], denses[3], denses[0], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 16)) vertlist[4] = VertexInterp(isolevel, positions[4], positions[5], denses[4], denses[5], densityNormals[pos.x, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 32)) vertlist[5] = VertexInterp(isolevel, positions[5], positions[6], denses[5], denses[6], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 64)) vertlist[6] = VertexInterp(isolevel, positions[6], positions[7], denses[6], denses[7], densityNormals[pos.x + 1, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 128)) vertlist[7] = VertexInterp(isolevel, positions[7], positions[4], denses[7], denses[4], densityNormals[pos.x, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 256)) vertlist[8] = VertexInterp(isolevel, positions[0], positions[4], denses[0], denses[4], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 512)) vertlist[9] = VertexInterp(isolevel, positions[1], positions[5], denses[1], denses[5], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 1024)) vertlist[10] = VertexInterp(isolevel, positions[2], positions[6], denses[2], denses[6], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x + 1, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 2048)) vertlist[11] = VertexInterp(isolevel, positions[3], positions[7], denses[3], denses[7], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); int submesh = densities.getMaterial(pos.x, pos.y, pos.z); for (int i = 0; triTable[cubeIndex][i] != -1; i += 3) { submeshIDList.Add(submesh); subMeshTriCount[submesh] = subMeshTriCount[submesh] + 1; triangleList.Add(new Triangle(vertlist[triTable[cubeIndex][i]][0], vertlist[triTable[cubeIndex][i + 1]][0], vertlist[triTable[cubeIndex][i + 2]][0], vertlist[triTable[cubeIndex][i]][1], vertlist[triTable[cubeIndex][i + 1]][1], vertlist[triTable[cubeIndex][i + 2]][1])); } }
/// <summary> /// Returns a node containing the point as close as possible to the requested LOD. /// </summary> /// <param name="pos"></param> /// <param name="searchLOD"></param> /// <returns>Null if no such node exists.</returns> public static Node[] searchNodeContainingDensity(Vector3 pos, int searchLOD) { Node[] ret = new Node[8]; for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { for (int z = 0; z < 3; z++) { if (topNodes[x, y, z] != null) topNodes[x, y, z].searchNodeCreate(pos, searchLOD, ref ret); } } } //Debug.Log("searchNodeContainingDensity triggered, pos:"+pos+"searchLOD:"+searchLOD+",return is"+ret); //这一条和下面的都是在NodeEditor里面会用,其他地方还没有看到 return ret; }