/* 文件名:OctreeClass.nut 路径:User/GameClass/OctreeClass/OctreeClass.nut 创建日期:2024-05-18 08:19 文件用途: */ class AABB { minX = null; minY = null; minZ = null; maxX = null; maxY = null; maxZ = null; ObjectIndex = null; constructor(...) { minX = vargv[0]; minY = vargv[1]; minZ = vargv[2]; maxX = vargv[3]; maxY = vargv[4]; maxZ = vargv[5]; if (vargv.len() > 6) ObjectIndex = vargv[6]; } } class OctreeNode { static m_maxObjects = 8; static m_maxDepth = 8; m_bounds = null; m_isLeaf = true; m_depth = 0; m_objects = null; m_children = null; constructor(bounds) { m_bounds = bounds; m_depth = 0; m_objects = []; m_children = [null, null, null, null, null, null, null, null]; } // function insert(aabb) { // if (!m_isLeaf) { // local index = getIndex(aabb); // if (index != -1) { // m_children[index].insert(aabb); // return; // } // } // m_objects.append(aabb); // if (m_objects.len() > m_maxObjects && m_depth< m_maxDepth) { // if (m_isLeaf) { // split(); // } // foreach(obj in m_objects) { // local index = getIndex(obj); // if (index != -1) { // m_children[index].insert(obj); // } // } // m_objects.clear(); // } // } function insert(aabb) { if (!this.m_isLeaf) { local index = this.getIndex(aabb); if (index != -1 && this.m_children[index]) { this.m_children[index].insert(aabb); return; } } if (this.m_objects.len() > this.m_maxObjects && this.m_depth< this.m_maxDepth) { if (this.m_isLeaf) { this.split(); } foreach(obj in this.m_objects) { local index = this.getIndex(obj); if (index != -1) { this.m_children[index].insert(obj); } } this.m_objects.clear(); } if (this.m_isLeaf) { this.m_objects.append(aabb); } } function clear() { for (local i = 0; i< m_children.len(); i++) { if (m_children[i] != null) { m_children[i].clear(); m_children[i] = null; } } /* foreach(child in m_children) { if (child != null) { child.clear(); // delete m_children.child; child = null; } } */ m_objects.clear(); } function retrieve(aabb) { local collidingObjects = []; local index = getIndex(aabb); if (index != -1 && !m_isLeaf) { local tmp = m_children[index].retrieve(aabb); collidingObjects.extend(tmp); } collidingObjects.extend(m_objects); return collidingObjects; } //判断该点是否在给定参数的立方体内 function pointIsInCubeArea(px, py, pz, startX, startY, startZ, endX, endY, endZ) { local cubeCenterX = (startX + endX) / 2; local cubeXLen = abs(startX - endX) / 2; local cubeCenterY = (startY + endY) / 2; local cubeYLen = abs(startY - endY) / 2; local cubeCenterZ = (startZ + endZ) / 2; local cubeZLen = abs(startZ - endZ) / 2; if (abs(px - cubeCenterX) <= cubeXLen && abs(py - cubeCenterY) <= cubeYLen && abs(pz - cubeCenterZ) <= cubeZLen) return true; return false; } //立方体与立方体之间碰撞 暂未测试 function CubeAndCubeCollection(c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ) { if (pointIsInCubeArea(c1StartX, c1StartY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1StartY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, c1EndY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1EndY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, c1StartY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1StartY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, c1EndY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1EndY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2StartY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2StartY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2EndY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2EndY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2StartY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2StartY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2EndY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2EndY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea((c1StartX + c1EndX) / 2, c1StartY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea((c1StartX + c1EndX) / 2, c1EndY, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea((c1StartX + c1EndX) / 2, c1StartY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea((c1StartX + c1EndX) / 2, c1EndY, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, (c1StartY + c1EndY) / 2, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, (c1StartY + c1EndY) / 2, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, (c1StartY + c1EndY) / 2, c1StartZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, (c1StartY + c1EndY) / 2, c1EndZ, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, c1StartY, (c1StartZ + c1EndZ) / 2, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1StartX, c1EndY, (c1StartZ + c1EndZ) / 2, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1StartY, (c1StartZ + c1EndZ) / 2, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea(c1EndX, c1EndY, (c1StartZ + c1EndZ) / 2, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea((c2StartX + c2EndX) / 2, c2StartY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea((c2StartX + c2EndX) / 2, c2EndY, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea((c2StartX + c2EndX) / 2, c2StartY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea((c2StartX + c2EndX) / 2, c2EndY, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, (c2StartY + c2EndY) / 2, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, (c2StartY + c2EndY) / 2, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, (c2StartY + c2EndY) / 2, c2StartZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, (c2StartY + c2EndY) / 2, c2EndZ, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2StartY, (c2StartZ + c2EndZ) / 2, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2StartX, c2EndY, (c2StartZ + c2EndZ) / 2, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2StartY, (c2StartZ + c2EndZ) / 2, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea(c2EndX, c2EndY, (c2StartZ + c2EndZ) / 2, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; if (pointIsInCubeArea((c1StartX + c1EndX) / 2, (c1StartY + c1EndY) / 2, (c1StartZ + c1EndZ) / 2, c2StartX, c2StartY, c2StartZ, c2EndX, c2EndY, c2EndZ)) return true; if (pointIsInCubeArea((c2StartX + c2EndX) / 2, (c2StartY + c2EndY) / 2, (c2StartZ + c2EndZ) / 2, c1StartX, c1StartY, c1StartZ, c1EndX, c1EndY, c1EndZ)) return true; return false; } function checkCollision(aabb1, aabb2) { return CubeAndCubeCollection(aabb1.minX, aabb1.minY, aabb1.minZ, aabb1.maxX, aabb1.maxY, aabb1.maxZ, aabb2.minX, aabb2.minY, aabb2.minZ, aabb2.maxX, aabb2.maxY, aabb2.maxZ); } function checkCollisionWithNode(aabb) { local collidingObjects = []; foreach(obj in m_objects) { if (checkCollision(obj, aabb)) { collidingObjects.append(obj); } } if (!m_isLeaf) { foreach(child in m_children) { if (child != null) { local tmp = child.checkCollisionWithNode(aabb); collidingObjects.extend(tmp); } } } return collidingObjects; } function split() { local minX = m_bounds.minX; local maxX = m_bounds.maxX; local minY = m_bounds.minY; local maxY = m_bounds.maxY; local minZ = m_bounds.minZ; local maxZ = m_bounds.maxZ; local midX = (minX + maxX) / 2; local midY = (minY + maxY) / 2; local midZ = (minZ + maxZ) / 2; local objectbuf = m_bounds.Object; m_children[0] = OctreeNode(AABB(minX, minY, minZ, midX, midY, midZ, objectbuf)); m_children[1] = OctreeNode(AABB(midX, minY, minZ, maxX, midY, midZ, objectbuf)); m_children[2] = OctreeNode(AABB(minX, midY, minZ, midX, maxY, midZ, objectbuf)); m_children[3] = OctreeNode(AABB(midX, midY, minZ, maxX, maxY, midZ, objectbuf)); m_children[4] = OctreeNode(AABB(minX, minY, midZ, midX, midY, maxZ, objectbuf)); m_children[5] = OctreeNode(AABB(midX, minY, midZ, maxX, midY, maxZ, objectbuf)); m_children[6] = OctreeNode(AABB(minX, midY, midZ, midX, maxY, maxZ, objectbuf)); m_children[7] = OctreeNode(AABB(midX, midY, midZ, maxX, maxY, maxZ, objectbuf)); m_isLeaf = false; } function getIndex(aabb) { local midX = (m_bounds.minX + m_bounds.maxX) / 2; local midY = (m_bounds.minY + m_bounds.maxY) / 2; local midZ = (m_bounds.minZ + m_bounds.maxZ) / 2; local top = aabb.minY > midY; local bottom = aabb.maxY< midY; local north = aabb.minZ > midZ; local south = aabb.maxZ< midZ; local east = aabb.minX > midX; local west = aabb.maxX< midX; if (top) { if (east) return 0; if (west) return 1; return -1; } if (bottom) { if (east) return 2; if (west) return 3; return -1; } if (north) { if (east) return 4; if (west) return 5; return -1; } if (south) { if (east) return 6; if (west) return 7; return -1; } return -1; } } // local octree = OctreeNode(AABB(0, 0, 0, 100, 100, 100)); // octree.clear(); // local aabb1 = AABB(10, 10, 10, 20, 20, 20); // local aabb2 = AABB(30, 30, 30, 40, 40, 40); // local aabb3 = AABB(50, 50, 50, 60, 60, 60); // octree.insert(aabb1); // octree.insert(aabb2); // octree.insert(aabb3); // local testAABB = AABB(15, 15, 15, 35, 35, 35); // local collidingObjects = octree.checkCollisionWithNode(testAABB); // print(collidingObjects.len()); // Util.PrintTable(collidingObjects); // foreach(obj in collidingObjects) { // print("Colliding object: (" + obj.minX + ", " + obj.minY + ", " + obj.minZ + ") - (" + // obj.maxX + ", " + obj.maxY + ", " + obj.maxZ + ")"); // }