Simple mesh triangles don't render properly (or at all) if axis aligned

When creating a dynamic mesh, if a triangle is axis aligned (i.e. the 3 vertices have identical X,Y or Z values), then it will fail to render properly. If the triangle is normal to the Y or Z axes, the mesh won’t render at all. If the triangle is normal to the X axis, then the side-length of the rendered triangle is too large by a multiple of the actual side length, i.e. if the triangle actually has side length 5, it will be rendered with side length 25. The meshpart properties (like Size, MeshSize, ExtentsSize), all have the correct dimensions, yet it appears much larger.

In all these cases, adjusting one of the vertices along the normal axis by a small amount fixes the problem (it actually appears, and at the right size), it’s only when the triangle becomes exactly axis aligned that it stops rendering properly (but I haven’t tested thoroughly to find other examples).

I don’t know if this is specific to DynamicMesh, or if this is a general bug with Roblox meshes. However this bug report from two years ago suggests it could be a general mesh bug.

Where: I don’t know if this is a studio bug or engine bug, since I can only test DynamicMesh in studio.
I am testing on an M2 mac, with Roblox Studio Version 0.583.2.5831071 (it is the native apple silicon app)
When: I first observed this yesterday, July 15.
Videos and images: Full repro code is at the bottom. This is the result of making the three axis-aligned triangles, as in the repro code. Only the red triangle, normal to the X-axis, is visible, and it is the wrong size (I included balls where the vertices should be). The other two triangles are not visible.

-- This one renders with side length SIZE^2, though it still
-- thinks its extents are Vector3.new(0.05, SIZE, SIZE)
makeMeshPart(
	"X-normal",
	Color3.new(1,0.6,0.6),
	Vector3.new(0, 0, 0),
	Vector3.new(0, 0, SIZE),
	Vector3.new(0, SIZE, 0)
)

-- This doesn't render at all
makeMeshPart(
	"Y-normal",
	Color3.new(0.6,1,0.6),
	Vector3.new(0, 0, 0),
	Vector3.new(0, 0, SIZE),
	Vector3.new(SIZE, 0, 0)
)

-- This doesn't render at all
makeMeshPart(
	"Z-normal",
	Color3.new(0.6,0.6,1),
	Vector3.new(0, 0, 0),
	Vector3.new(0, SIZE, 0),
	Vector3.new(SIZE, 0, 0)
)

Here I have added EPS=0.1 to one of the vertices, in the normal-axis coordinate. This resolves the bug (but of course now the triangles are off-axis).

-- This renders correctly
makeMeshPart(
	"X-normal-plus-epsilon",
	Color3.new(1,0,0),
	Vector3.new(0, 0, 0),
	Vector3.new(EPS, 0, SIZE),
	Vector3.new(0, SIZE, 0)
)

-- This renders correctly
makeMeshPart(
	"Y-normal-plus-epsilon",
	Color3.new(0,1,0),
	Vector3.new(0, 0, 0),
	Vector3.new(0, EPS, SIZE),
	Vector3.new(SIZE, 0, 0)
)

-- This renders correctly
makeMeshPart(
	"Z-normal-plus-epsilon",
	Color3.new(0,0,1),
	Vector3.new(0, 0, 0),
	Vector3.new(0, SIZE, EPS),
	Vector3.new(SIZE, 0, 0)
)

Repro

local HEIGHT = 10
local EPS = 0.1
local SIZE = 5

local function makeMeshPart(name, color, v1, v2, v3)
	local dynamicMesh = Instance.new("DynamicMesh")

	local index1 = dynamicMesh:AddVertex(v1)
	local index2 = dynamicMesh:AddVertex(v2)
	local index3 = dynamicMesh:AddVertex(v3)
	
	local u = v2 - v1
	local v = v3 - v1
	local normal = u:Cross(v).Unit
	
	dynamicMesh:SetVertexNormal(index1, normal)
	dynamicMesh:SetVertexNormal(index2, normal)
	dynamicMesh:SetVertexNormal(index3, normal)
	
	dynamicMesh:SetVertexColor(index1, color)
	dynamicMesh:SetVertexColor(index2, color)
	dynamicMesh:SetVertexColor(index3, color)

	dynamicMesh:AddTriangle(index1, index2, index3)
	-- Second one in different order so it's double-sided.
	dynamicMesh:AddTriangle(index1, index3, index2)

	dynamicMesh.Parent = workspace
	
	local meshPart = dynamicMesh:CreateMeshPartAsync(Enum.CollisionFidelity.Box)
	meshPart.Name = name
	meshPart.Anchored = true
	meshPart.Position = Vector3.new(0, HEIGHT, 0)
	meshPart.Parent = workspace
end

local function makeBall(v)
	local ball = Instance.new("Part")
	ball.Anchored = true
	ball.Shape = Enum.PartType.Ball
	ball.Size = Vector3.new(0.5,0.5,0.5)
	ball.Position = v
	ball.Parent = workspace
end

-- Points of reference
makeBall(Vector3.new(0,HEIGHT,0))
makeBall(Vector3.new(SIZE,HEIGHT,0))
makeBall(Vector3.new(0,SIZE+HEIGHT,0))
makeBall(Vector3.new(0,HEIGHT,SIZE))

-- This one renders with side length SIZE^2, though it still
-- thinks its extents are Vector3.new(0.05, SIZE, SIZE)
makeMeshPart(
	"X-normal",
	Color3.new(1,0.6,0.6),
	Vector3.new(0, 0, 0),
	Vector3.new(0, 0, SIZE),
	Vector3.new(0, SIZE, 0)
)

-- This doesn't render at all
makeMeshPart(
	"Y-normal",
	Color3.new(0.6,1,0.6),
	Vector3.new(0, 0, 0),
	Vector3.new(0, 0, SIZE),
	Vector3.new(SIZE, 0, 0)
)

-- This doesn't render at all
makeMeshPart(
	"Z-normal",
	Color3.new(0.6,0.6,1),
	Vector3.new(0, 0, 0),
	Vector3.new(0, SIZE, 0),
	Vector3.new(SIZE, 0, 0)
)

-- Same as the last three, but with EPS added to normal axis
-- in one of the vertices

-- This renders correctly
makeMeshPart(
	"X-normal-plus-epsilon",
	Color3.new(1,0,0),
	Vector3.new(0, 0, 0),
	Vector3.new(EPS, 0, SIZE),
	Vector3.new(0, SIZE, 0)
)

-- This renders correctly
makeMeshPart(
	"Y-normal-plus-epsilon",
	Color3.new(0,1,0),
	Vector3.new(0, 0, 0),
	Vector3.new(0, EPS, SIZE),
	Vector3.new(SIZE, 0, 0)
)

-- This renders correctly
makeMeshPart(
	"Z-normal-plus-epsilon",
	Color3.new(0,0,1),
	Vector3.new(0, 0, 0),
	Vector3.new(0, SIZE, EPS),
	Vector3.new(SIZE, 0, 0)
)
4 Likes

Thanks for the report! I filed a ticket in our internal database.

3 Likes

Finally got around to this one. This has been fixed in and will be released ~2 weeks from now.

Also, I should mention. @blinkybool Thank you so much for the detailed test case, this made finding and fixing the issue a lot easier!

2 Likes

Yay! Can you tell us what it was?

The squared size was due to Roblox internally tracking the visual size as well as the data model size (basically the size you see in studio). So for example adding vertices (0, 0, 0) and (1, 1, 5) would make the visual model size (1,1,5). In cases where the visual size doesn’t match the data model size, the actual size is used as a scaling matrix. So when this mismatches it basically multiplies the visual size with the data model size. The data model size internally clamps size to 0.001, so that it has some sort of values to work with, but the visual size didn’t do this exact same clamping causing them to be able to be different and hence double scale. Adding the same clamping logic to the visual part solved this.

The flat planes not showing up could either be a culling issue or the geometry never being added due to 0 volume, but I never needed to debug it as the fix for the square also resolved the culling.

3 Likes