I’d like to know how to find if two vector3 volumes are intersecting. These would be oriented around parts in the workspace, but I cant use getpartsinpart, as this is using imaginary volumes.
I’d also like to know if it’s possible to do the same with vector3 volumes and baseparts of any shape.
For between two cubes get all the corner points of one and check if each point overlaps with the other cube. This should work for rotated ones as well. There’s plenty of examples on the forum of getting the corners of a cube. I’ll guess and say you’re trying to use this for your voxel module. This is kinda what I did for mine with using imaginary parts and then greedy meshing and finally creating parts. It’s pretty performant.
Here’s some example code for checking if a point is in a cube:
function isPointInCube(position, brickCFrame, brickSize)
local v3 = brickCFrame:PointToObjectSpace(position)
return (math.abs(v3.X) <= brickSize.X / 2)
and (math.abs(v3.Y) <= brickSize.Y / 2)
and (math.abs(v3.Z) <= brickSize.Z / 2)
end
Thanks, this works. But I’m also wondering if you know of a way to greedy mesh a table of part information. I’m using these types:
type VoxelInfo = {
AlreadyExistsInWorkspace:boolean,
Size:Vector3,
CFrame:CFrame,
CanCollide:boolean,
Parent:Instance,
Color:Color3,
Material:Enum.Material,
Anchored:boolean,
Transparency:number,
AlreadyDivided:boolean
}
type VoxelInfoTable = {VoxelInfo}
And essentially I’m trying to loop through the infoTable to greedymesh everything within the table before actually constructing the parts. I’ve looked into greedymeshing resources and they seem to be based around the parts already being instanced within the game. This is kind of unrelated to the original question but I noticed you have experience with greedymeshing so I figured I’d ask anyways.
With “Vector3 volume”, do you mean the region inside an imaginary part?
When both parts are convex polyhedra, the separating axis theorem can be used. I wrote a simpler version that only works for box-box pairs and a more general and less efficient version that also works for wedges and corner wedges. Here’s a Stackexchange question with an answer that helped me understand the logic behind using this theorem to check whether convex polyhedra intersect.
Collisions between a sphere and another shape can be tested by calculating the point on the other shape that is closest to the sphere center and comparing the distance between the sphere center and that point to the sphere radius. We can also compare the square of the distance to the square of the radius because if a >= 0 and b >= 0 and a <= b then a^2 <= b^2.
I’m not sure whether my code is entirely correct but based on my testing so far it seems to work correctly. If my closest point calculation code for the sphere collision checks doesn’t work correctly in some cases, you can also try the closest point code in this tutorial.
My code is made for the primitive roblox shapes but the code for convex polyhedron primitives could be modified to work for arbitrary convex polyhedra. If you have concave polyhedra (none of Roblox’s primitive shapes (Enum.PartType) are concave, though), then I think you could check whether any edge of either of them intersects any face of the other (you’d have to do the same check for both permutations of the shape pair i.e. check whether any edge of shape 1 intersects any face of shape 2 or any edge of shape 2 intersects any face of shape 1). If no intersects are found this way, you’d also need to check whether either of them is completely inside the other by just choosing one vertex of both and checking whether that vertex is in the other polyhedron.
Unfortunately, I don’t know how to check for collisions between a pair of cylinders or a cylinder and a polyhedron.
Here’s my code.
BoxBoxIntersectionQuery ModuleScript
--!strict
local BoxBoxIntersectionQuery = {}
local function areProjectionsOverlapping(cf1: CFrame, size1: Vector3, cf2: CFrame, size2: Vector3, projectionVector: Vector3): boolean
local posCoord1: number = cf1.Position:Dot(projectionVector)
local halfLength1: number = .5 * Vector3.new(cf1.XVector:Dot(projectionVector), cf1.YVector:Dot(projectionVector), cf1.ZVector:Dot(projectionVector)):Abs():Dot(size1)
local minCoord1: number, maxCoord1: number = posCoord1 - halfLength1, posCoord1 + halfLength1
local posCoord2: number = cf2.Position:Dot(projectionVector)
local halfLength2: number = .5 * Vector3.new(cf2.XVector:Dot(projectionVector), cf2.YVector:Dot(projectionVector), cf2.ZVector:Dot(projectionVector)):Abs():Dot(size2)
local minCoord2: number, maxCoord2: number = posCoord2 - halfLength2, posCoord2 + halfLength2
return maxCoord1 > minCoord2 and minCoord1 < maxCoord2
end
-- In the case of boxes, for every edge, there's a normal that is parallel to it.
-- So although the second step of using SAT (separating axis theorem) for checking for collision between
-- convex polyhedra is doing projection overlap tests with projection axes that
-- are perpendicular to edge pairs, when the polyhedra are boxes, we can use the normal vectors as edge vectors.
function BoxBoxIntersectionQuery.doBoxesIntersect(cf1: CFrame, size1: Vector3, cf2: CFrame, size2: Vector3): boolean
local axes: {Enum.Axis} = Enum.Axis:GetEnumItems()
local rot1: CFrame, rot2: CFrame = cf1.Rotation, cf2.Rotation
for _, axis: Enum.Axis in axes do
local axisVector: Vector3 = Vector3.FromAxis(axis)
local box1Normal: Vector3 = rot1 * axisVector
if not areProjectionsOverlapping(cf1, size1, cf2, size2, box1Normal) then
return false
end
local box2Normal: Vector3 = rot2 * axisVector
if not areProjectionsOverlapping(cf1, size1, cf2, size2, box2Normal) then
return false
end
for _, otherAxis: Enum.Axis in axes do
local otherAxisVector: Vector3 = Vector3.FromAxis(otherAxis)
local box2OtherNormal: Vector3 = rot2 * otherAxisVector
local crossProduct: Vector3 = box1Normal:Cross(box2OtherNormal)
if math.abs(crossProduct:Dot(crossProduct)) < 1e-4 then
continue
end
if not areProjectionsOverlapping(cf1, size1, cf2, size2, crossProduct) then
return false
end
end
end
return true
end
return BoxBoxIntersectionQuery
--!strict
local ConvexPolyhedronPrimitivesIntersectionQuery = {}
local faceNormalsForShapes: {[Enum.PartType]: {{Vector3}}} = {
[Enum.PartType.Block] = {
{Vector3.xAxis, Vector3.zero, Vector3.zero},
{Vector3.zero, Vector3.yAxis, Vector3.zero},
{Vector3.zero, Vector3.zero, Vector3.zAxis}
},
[Enum.PartType.Wedge] = {
{Vector3.xAxis, Vector3.zero, Vector3.zero},
{Vector3.zero, Vector3.yAxis, Vector3.zero},
{Vector3.zero, Vector3.zero, Vector3.zAxis},
{Vector3.zero, Vector3.zAxis, -Vector3.yAxis}
},
[Enum.PartType.CornerWedge] = {
{Vector3.xAxis, Vector3.zero, Vector3.zero},
{Vector3.zero, Vector3.yAxis, Vector3.zero},
{Vector3.zero, Vector3.zero, Vector3.zAxis},
{Vector3.zero, Vector3.zAxis, Vector3.yAxis},
{-Vector3.yAxis, Vector3.xAxis, Vector3.zero}
}
}
local edgeVectorsForShapes: {[Enum.PartType]: {Vector3}} = {
[Enum.PartType.Block] = {
Vector3.xAxis,
Vector3.yAxis,
Vector3.zAxis
},
[Enum.PartType.Wedge] = {
Vector3.xAxis,
Vector3.yAxis,
Vector3.zAxis,
Vector3.new(0, 1, 1)
},
[Enum.PartType.CornerWedge] = {
Vector3.xAxis,
Vector3.yAxis,
Vector3.zAxis,
Vector3.new(0, 1, -1),
Vector3.new(1, 1, -1),
Vector3.new(1, 1, 0)
}
}
local verticesForShapes: {[Enum.PartType]: {Vector3}} = {
[Enum.PartType.Block] = {
Vector3.new(-1, -1, -1),
Vector3.new(-1, -1, 1),
Vector3.new(-1, 1, -1),
Vector3.new(-1, 1, 1),
Vector3.new(1, -1, -1),
Vector3.new(1, -1, 1),
Vector3.new(1, 1, -1),
Vector3.new(1, 1, 1)
},
[Enum.PartType.Wedge] = {
Vector3.new(-1, -1, -1),
Vector3.new(-1, -1, 1),
Vector3.new(-1, 1, 1),
Vector3.new(1, -1, -1),
Vector3.new(1, -1, 1),
Vector3.new(1, 1, 1)
},
[Enum.PartType.CornerWedge] = {
Vector3.new(-1, -1, -1),
Vector3.new(-1, -1, 1),
Vector3.new(1, -1, -1),
Vector3.new(1, -1, 1),
Vector3.new(1, 1, -1)
}
}
local function doProjectionsOverlap(shape1: Enum.PartType, cf1: CFrame, size1: Vector3, shape2: Enum.PartType, cf2: CFrame, size2: Vector3, projectionVector: Vector3): boolean
local shape1MinCoord: number, shape1MaxCoord: number = math.huge, -math.huge
for _, shape1GeneralizedVertex: Vector3 in verticesForShapes[shape1] do
local vertex: Vector3 = cf1 * (.5 * shape1GeneralizedVertex * size1)
local coord: number = vertex:Dot(projectionVector)
if coord < shape1MinCoord then
shape1MinCoord = coord
end
if coord > shape1MaxCoord then
shape1MaxCoord = coord
end
end
local shape2MinCoord: number, shape2MaxCoord: number = math.huge, -math.huge
for _, shape2GeneralizedVertex: Vector3 in verticesForShapes[shape2] do
local vertex: Vector3 = cf2 * (.5 * shape2GeneralizedVertex * size2)
local coord: number = vertex:Dot(projectionVector)
if coord < shape2MinCoord then
shape2MinCoord = coord
end
if coord > shape2MaxCoord then
shape2MaxCoord = coord
end
end
return shape1MaxCoord > shape2MinCoord and shape1MinCoord < shape2MaxCoord
end
function ConvexPolyhedronPrimitivesIntersectionQuery.doConvexPolyhedronPrimitivesIntersect(shape1: Enum.PartType, cf1: CFrame, size1: Vector3, shape2: Enum.PartType, cf2: CFrame, size2: Vector3): boolean
local rot1: CFrame, rot2: CFrame = cf1.Rotation, cf2.Rotation
for _, shape1GeneralizedNormal: {Vector3} in faceNormalsForShapes[shape1] do
local normal: Vector3 = rot1 * Vector3.new(size1:Dot(shape1GeneralizedNormal[1]), size1:Dot(shape1GeneralizedNormal[2]), size1:Dot(shape1GeneralizedNormal[3]))
if not doProjectionsOverlap(shape1, cf1, size1, shape2, cf2, size2, normal) then
return false
end
end
for _, shape2GeneralizedNormal: {Vector3} in faceNormalsForShapes[shape2] do
local normal: Vector3 = rot2 * Vector3.new(size2:Dot(shape2GeneralizedNormal[1]), size2:Dot(shape2GeneralizedNormal[2]), size2:Dot(shape2GeneralizedNormal[3]))
if not doProjectionsOverlap(shape1, cf1, size1, shape2, cf2, size2, normal) then
return false
end
end
for _, shape1GeneralizedEdgeVector: Vector3 in edgeVectorsForShapes[shape1] do
local shape1EdgeVector: Vector3 = rot1 * (shape1GeneralizedEdgeVector * size1)
for _, shape2GeneralizedEdgeVector: Vector3 in edgeVectorsForShapes[shape2] do
local shape2EdgeVector: Vector3 = rot2 * (shape2GeneralizedEdgeVector * size2)
local crossProduct: Vector3 = shape1EdgeVector:Cross(shape2EdgeVector)
if math.abs(crossProduct:Dot(crossProduct)) < 1e-4 then
continue
end
if not doProjectionsOverlap(shape1, cf1, size1, shape2, cf2, size2, crossProduct) then
return false
end
end
end
return true
end
return ConvexPolyhedronPrimitivesIntersectionQuery
SphereIntersectionQueries ModuleScript
--!strict
local SphereIntersectionQueries = {}
function SphereIntersectionQueries.doSpheresIntersect(cf1: CFrame, size1: Vector3, cf2: CFrame, size2: Vector3): boolean
local radius1: number, radius2: number = .5 * math.min(size1.X, size1.Y, size1.Z), .5 * math.min(size2.X, size2.Y, size2.Z)
local vectorBetweenCenters: Vector3 = cf2.Position - cf1.Position
-- Checking whether squared distance between centers is greater than squared sum of radii.
return vectorBetweenCenters:Dot(vectorBetweenCenters) <= (radius1 + radius2) * (radius1 + radius2)
end
-- This function and the functions below calculate the point in the other shape closest to the sphere center
-- and compare the squared distance between that point and the sphere center to the square radius of the sphere.
function SphereIntersectionQueries.doSphereAndBoxIntersect(sphereCf: CFrame, sphereSize: Vector3, boxCf: CFrame, boxSize: Vector3): boolean
local sphereRadius: number = .5 * math.min(sphereSize.X, sphereSize.Y, sphereSize.Z)
local sphereCenterInBoxLocalSpace: Vector3 = boxCf:PointToObjectSpace(sphereCf.Position)
local halfBoxSize: Vector3 = .5 * boxSize
local localSpacePointInBoxClosestToSphereCenter: Vector3 = Vector3.new(
math.clamp(sphereCenterInBoxLocalSpace.X, -halfBoxSize.X, halfBoxSize.X),
math.clamp(sphereCenterInBoxLocalSpace.Y, -halfBoxSize.Y, halfBoxSize.Y),
math.clamp(sphereCenterInBoxLocalSpace.Z, -halfBoxSize.Z, halfBoxSize.Z)
)
local vectorFromClosestPointToSphereCenter: Vector3 = sphereCenterInBoxLocalSpace - localSpacePointInBoxClosestToSphereCenter
return vectorFromClosestPointToSphereCenter:Dot(vectorFromClosestPointToSphereCenter) <= sphereRadius * sphereRadius
end
function SphereIntersectionQueries.doSphereAndCylinderIntersect(sphereCf: CFrame, sphereSize: Vector3, cylinderCf: CFrame, cylinderSize: Vector3): boolean
if sphereCf.Position:FuzzyEq(cylinderCf.Position) then
-- This is done to prevent a NAN vector in the calculations.
return true
end
local sphereRadius: number = .5 * math.min(sphereSize.X, sphereSize.Y, sphereSize.Z)
local cylinderRadius: number = .5 * math.min(cylinderSize.Y, cylinderSize.Z)
local sphereCenterInCylinderLocalSpace: Vector3 = cylinderCf:PointToObjectSpace(sphereCf.Position)
local sphereYZCenterInCylinderLocalSpace: Vector3 = sphereCenterInCylinderLocalSpace * Vector3.new(0, 1, 1)
local closestPointYZ: Vector3 = sphereYZCenterInCylinderLocalSpace.Unit * math.min(sphereYZCenterInCylinderLocalSpace.Magnitude, cylinderRadius)
local localSpacePointInCylinderClosestToSphereCenter: Vector3 = Vector3.new(
math.clamp(sphereCenterInCylinderLocalSpace.X, -.5 * cylinderSize.X, .5 * cylinderSize.X),
closestPointYZ.Y,
closestPointYZ.Z
)
local vectorFromClosestPointToSphereCenter: Vector3 = sphereCenterInCylinderLocalSpace - localSpacePointInCylinderClosestToSphereCenter
--print(vectorFromClosestPointToSphereCenter)
return vectorFromClosestPointToSphereCenter:Dot(vectorFromClosestPointToSphereCenter) <= sphereRadius * sphereRadius
end
function SphereIntersectionQueries.doSphereAndWedgeIntersect(sphereCf: CFrame, sphereSize: Vector3, wedgeCf: CFrame, wedgeSize: Vector3): boolean
local sphereRadius: number = .5 * math.min(sphereSize.X, sphereSize.Y, sphereSize.Z)
local sphereCenterInWedgeLocalSpace: Vector3 = wedgeCf:PointToObjectSpace(sphereCf.Position)
local closestPoint: Vector3 = sphereCenterInWedgeLocalSpace
local slantedFaceNormalInLocalSpace: Vector3 = Vector3.new(0, wedgeSize.Z, -wedgeSize.Y).Unit
local signedDistFromSlantedFacePlane: number = sphereCenterInWedgeLocalSpace:Dot(slantedFaceNormalInLocalSpace)
if signedDistFromSlantedFacePlane > 0 then
-- In this case sphereCenterInWedgeLocalSpace is guaranteed to be outside the wedge
-- and the closest point is guaranteed to be on the slanted face
-- ("on the slanted face" in this case can also mean on the edge of the slanted face).
closestPoint -= signedDistFromSlantedFacePlane * slantedFaceNormalInLocalSpace
end
closestPoint = Vector3.new(
math.clamp(closestPoint.X, -.5 * wedgeSize.X, .5 * wedgeSize.X),
math.clamp(closestPoint.Y, -.5 * wedgeSize.Y, .5 * wedgeSize.Y),
math.clamp(closestPoint.Z, -.5 * wedgeSize.Z, .5 * wedgeSize.Z)
)
local vectorFromClosestPointToSphereCenter: Vector3 = sphereCenterInWedgeLocalSpace - closestPoint
return vectorFromClosestPointToSphereCenter:Dot(vectorFromClosestPointToSphereCenter) <= sphereRadius * sphereRadius
end
function SphereIntersectionQueries.doSphereAndCornerWedgeIntersect(sphereCf: CFrame, sphereSize: Vector3, cornerWedgeCf: CFrame, cornerWedgeSize: Vector3): boolean
local sphereRadius: number = .5 * math.min(sphereSize.X, sphereSize.Y, sphereSize.Z)
local sphereCenterInCornerWedgeLocalSpace: Vector3 = cornerWedgeCf:PointToObjectSpace(sphereCf.Position)
local closestPoint: Vector3 = sphereCenterInCornerWedgeLocalSpace
local firstSlantedFaceNormal: Vector3 = Vector3.new(0, cornerWedgeSize.Z, cornerWedgeSize.Y).Unit
local signedDistFromFirstSlantedFacePlane: number = closestPoint:Dot(firstSlantedFaceNormal)
local isAboveFirstSlantedFacePlane: boolean = signedDistFromFirstSlantedFacePlane > 0
local secondSlantedFaceNormal: Vector3 = Vector3.new(-cornerWedgeSize.Y, cornerWedgeSize.X, 0).Unit
local signedDistFromSecondSlantedFacePlane: number = closestPoint:Dot(secondSlantedFaceNormal)
local isAboveSecondSlantedFacePlane: boolean = signedDistFromSecondSlantedFacePlane > 0
if isAboveFirstSlantedFacePlane and isAboveSecondSlantedFacePlane then
-- Since the external angle between these faces is less than 270 degrees,
-- I think (but I'm not sure at all) it may be necessary to
-- check whether the point is above either of the faces' half-planes that are limited by the
-- intersection line of the faces, and then if it is, project it to
-- that half-plane, and if it isn't project it to the intersection line of the face planes.
-- Any other face pairs of the part have an external angle of at least 270 degrees between them.
-- isAboveFirstSlantedFaceHalfPlane and isAboveSecondSlantedFaceHalfPlane are mutually exclusive because the external
-- angle between the faces is at least 180 degrees (otherwise this wouldn't be a convex polyhedron).
local planeIntersectionLineVector: Vector3 = Vector3.new(cornerWedgeSize.X, cornerWedgeSize.Y, -cornerWedgeSize.Z)
local firstSlantedFaceEdgeNormal: Vector3 = planeIntersectionLineVector:Cross(firstSlantedFaceNormal)
local isAboveFirstSlantedFaceHalfPlane: boolean = closestPoint:Dot(firstSlantedFaceEdgeNormal) > 0
if isAboveFirstSlantedFaceHalfPlane then
closestPoint -= signedDistFromFirstSlantedFacePlane * firstSlantedFaceNormal
else
local secondSlantedFaceEdgeNormal: Vector3 = secondSlantedFaceNormal:Cross(planeIntersectionLineVector)
local isAboveSecondSlantedFaceHalfPlane: boolean = closestPoint:Dot(secondSlantedFaceEdgeNormal) > 0
if isAboveSecondSlantedFaceHalfPlane then
closestPoint -= signedDistFromSecondSlantedFacePlane * secondSlantedFaceNormal
else
local intersectionLineDirection: Vector3 = planeIntersectionLineVector.Unit
closestPoint = closestPoint:Dot(intersectionLineDirection) * intersectionLineDirection
end
end
elseif isAboveFirstSlantedFacePlane then
closestPoint -= signedDistFromFirstSlantedFacePlane * firstSlantedFaceNormal
elseif isAboveSecondSlantedFacePlane then
closestPoint -= signedDistFromSecondSlantedFacePlane * secondSlantedFaceNormal
end
closestPoint = Vector3.new(
math.clamp(closestPoint.X, -.5 * cornerWedgeSize.X, .5 * cornerWedgeSize.X),
math.clamp(closestPoint.Y, -.5 * cornerWedgeSize.Y, .5 * cornerWedgeSize.Y),
math.clamp(closestPoint.Z, -.5 * cornerWedgeSize.Z, .5 * cornerWedgeSize.Z)
)
local vectorFromClosestPointToSphereCenter: Vector3 = sphereCenterInCornerWedgeLocalSpace - closestPoint
return vectorFromClosestPointToSphereCenter:Dot(vectorFromClosestPointToSphereCenter) <= sphereRadius * sphereRadius
end
return SphereIntersectionQueries
PrimitiveIntersectionQuery ModuleScript (this is the one that is meant to be used by external code)
--!strict
local primitiveIntersectionModulesFolder = script.Parent
local BoxBoxIntersectionQuery = require(primitiveIntersectionModulesFolder.BoxBoxIntersectionQuery)
local ConvexPolyhedronPrimitivesIntersectionQuery = require(primitiveIntersectionModulesFolder.ConvexPolyhedronPrimitivesIntersectionQuery)
local SphereIntersectionQueries = require(primitiveIntersectionModulesFolder.SphereIntersectionQueries)
local PrimitiveIntersectionQuery = {}
function PrimitiveIntersectionQuery.doShapesIntersect(shape1: Enum.PartType, cf1: CFrame, size1: Vector3, shape2: Enum.PartType, cf2: CFrame, size2: Vector3): boolean
if shape1 == Enum.PartType.Ball or shape2 == Enum.PartType.Ball then
if shape2 == Enum.PartType.Ball then
-- Doing these guarantees that the first shape is a sphere.
shape1, shape2 = shape2, shape1
cf1, cf2 = cf2, cf1
size1, size2 = size2, size1
end
return
if shape2 == Enum.PartType.Ball then SphereIntersectionQueries.doSpheresIntersect(cf1, size1, cf2, size2)
elseif shape2 == Enum.PartType.Cylinder then SphereIntersectionQueries.doSphereAndCylinderIntersect(cf1, size1, cf2, size2)
elseif shape2 == Enum.PartType.Block then SphereIntersectionQueries.doSphereAndBoxIntersect(cf1, size1, cf2, size2)
elseif shape2 == Enum.PartType.Wedge then SphereIntersectionQueries.doSphereAndWedgeIntersect(cf1, size1, cf2, size2)
else SphereIntersectionQueries.doSphereAndCornerWedgeIntersect(cf1, size1, cf2, size2)
elseif shape1 == Enum.PartType.Cylinder or shape2 == Enum.PartType.Cylinder then
error("Unsupported shape pair.")
elseif shape1 == Enum.PartType.Block and shape2 == Enum.PartType.Block then
return BoxBoxIntersectionQuery.doBoxesIntersect(cf1, size1, cf2, size2)
else
return ConvexPolyhedronPrimitivesIntersectionQuery.doConvexPolyhedronPrimitivesIntersect(shape1, cf1, size1, shape2, cf2, size2)
end
end
return PrimitiveIntersectionQuery
A test script (creates parts of different shapes which can then be moved, rotated and resized with studio tools during runtime, and prints which one's are intersecting after the CFrame or Size of any of them is changed)
--!strict
local PrimitiveIntersectionQuery = require(script.Parent.PrimitiveIntersectionQuery)
local firstPartPos: Vector3 = Vector3.new(8, .5, 0)
local offsetBetweenInitialPositions: Vector3 = Vector3.new(8, 0, 0)
local box1: Part = Instance.new("Part")
box1.Name = "Box1"
box1.Color = Color3.new(1, 0, 0)
box1.Size = Vector3.new(4, 1, 2)
box1.Position = firstPartPos
box1.Anchored = true
box1.Parent = workspace
local box2: Part = Instance.new("Part")
box2.Name = "Box2"
box2.Color = Color3.new(1, .4, .6)
box2.Size = Vector3.new(4, 1, 2)
box2.Position = firstPartPos + offsetBetweenInitialPositions
box2.Anchored = true
box2.Parent = workspace
local wedge: Part = Instance.new("Part")
wedge.Name = "Wedge"
wedge.Color = Color3.new(1, .5, 0)
wedge.Size = Vector3.new(4, 1, 2)
wedge.Position = firstPartPos + 2 * offsetBetweenInitialPositions
wedge.Shape = Enum.PartType.Wedge
wedge.Anchored = true
wedge.Parent = workspace
local cornerWedge: Part = Instance.new("Part")
cornerWedge.Name = "CornerWedge"
cornerWedge.Color = Color3.new(1, 1, 0)
cornerWedge.Size = Vector3.new(4, 1, 2)
cornerWedge.Position = firstPartPos + 3 * offsetBetweenInitialPositions
cornerWedge.Shape = Enum.PartType.CornerWedge
cornerWedge.Anchored = true
cornerWedge.Parent = workspace
local sphere: Part = Instance.new("Part")
sphere.Name = "Sphere"
sphere.Color = Color3.new(0, 1, 0)
sphere.BottomSurface, sphere.TopSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
sphere.Size = Vector3.new(1, 1, 1)
sphere.Position = firstPartPos + 4 * offsetBetweenInitialPositions
sphere.Shape = Enum.PartType.Ball
sphere.Anchored = true
sphere.Parent = workspace
local cylinder: Part = Instance.new("Part")
cylinder.Name = "Cylinder"
cylinder.Color = Color3.new(0, 0, 1)
cylinder.BottomSurface, cylinder.TopSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
cylinder.Size = Vector3.new(4, 1, 1)
cylinder.Position = firstPartPos + 5 * offsetBetweenInitialPositions
cylinder.Shape = Enum.PartType.Cylinder
cylinder.Anchored = true
cylinder.Parent = workspace
local parts: {Part} = {box1, box2, wedge, cornerWedge, sphere, cylinder}
local function isPairValid(part: Part, otherPart: Part): boolean
if (part.Shape == Enum.PartType.Cylinder and otherPart.Shape ~= Enum.PartType.Ball) or (otherPart.Shape == Enum.PartType.Cylinder and part.Shape ~= Enum.PartType.Ball) then
return false
end
return true
end
local function onChanged(): ()
local intersectingPairsString: string = ""
for iPart, part: Part in parts do
for iOtherPart: number = iPart + 1, #parts do
local otherPart: Part = parts[iOtherPart]
if not isPairValid(part, otherPart) then
continue
end
local doPartsIntersect: boolean = PrimitiveIntersectionQuery.doShapesIntersect(part.Shape, part.CFrame, part.Size, otherPart.Shape, otherPart.CFrame, otherPart.Size)
if doPartsIntersect then
if intersectingPairsString ~= "" then
intersectingPairsString ..= ", "
end
intersectingPairsString ..= `({part.Name}, {otherPart.Name})`
end
end
end
print(`Intersecting part pairs: {intersectingPairsString}`)
end
for _, part: Part in parts do
part:GetPropertyChangedSignal("CFrame"):Connect(onChanged)
part:GetPropertyChangedSignal("Size"):Connect(onChanged)
end
If code that works for non-rotated boxes is sufficient then the OP of the following topic has code for that.
@progamers246810 , checking whether at least one corner of one box is in the other box is not always enough. Here are some examples of situations where that isn’t sufficient. In these cases, neither of the boxes has any vertices inside the other but the boxes still intersect. In one of the cases the boxes aren’t even rotated so your suggestion isn’t reliable even for non-rotated boxes.
However, I’m confused about what this intersection check code will be used for (is it somehow used in the greedy meshing or is that an entirely separate problem?) so I’m not sure whether your code still works for OP’s use case.
This is definitely a more in depth response. I know SAT is the way to go in situations like this but I was just trying to go with the more easier route. Obviously I didn’t think it through enough.
I’m making a voxel destruction system. This would be based around basepart hitboxes, which would detect for parts and divide them according to where the hitbox collided with the part. The idea is instead of dividing parts through continuously instancing new parts to make up the volume of the original part, which would inevitably leave tons of invisible parts behind. Instead of this, the idea is to completely remove all invisible parts, which would mean dividing the vector3 volumes of the parts continuously through tables, and removing all unnecessary parts in the table. This would save a ton on performance, as there is no actual instancing and all the calculations are done within tables. The idea behind checking if the hitbox volume and the vector3 volume of the part are colliding, is so that I can detect if the hitbox would be intersecting with this new imaginary part created by the division. Because the parts don’t actually exist, I can’t use GetPartsInPart.
And as for greedy meshing, that is a seperate issue. Performance would improve even more if I could process the table of voxels through a greedy meshing function before constructing them.
Thing is the way I did destruction was with perfect voxels . While it seems your destruction system is for all part sizes. I was able to use a grid because I was splitting parts into equal 1x1x1 voxels. Your best bet would to do something like this system: GitHub - royal0959/rblx-nonvoxel-greedy-meshing: module for merging parts that don't need to be adhere to voxel grids. But you would have to find a way to get nearby tables without using spatial queries. In another post I showed how I did greedy meshing I’ll link that as well if you also want to look at that.
An idea I have is you could loop through all your tables after checking which ones aren’t in the hitbox, then group parts into different tables if they share the same properties. Then using size and their position you could figure out if they are pretty much touching each other then merge them together. With the way your voxel destruction works “normal” greedy meshing isn’t going to cut it.