Finding 2d bounding box of 3dMeshInstance

Godot Version

4.2.2

Question

I’m trying to calculate a bounding box for a 3d mesh so I can draw a box around it in screen-space I’ve got something close to right but not quite right, anyone see what needs to be changed?

static Rect2 GetScreenRect(MeshInstance3D mesh)
{
	Aabb box = mesh.GetAabb();
	Vector3 worldStart = mesh.GlobalTransform * box.Position;
	Vector3 worldEnd = mesh.GlobalTransform * box.End;
//Global.CurrentEnvironment.MainCamera just points to the camera thats rendering to the screen
	Vector2 start = Global.CurrentEnvironment.MainCamera.UnprojectPosition(worldStart);
	Vector2 end = Global.CurrentEnvironment.MainCamera.UnprojectPosition(worldEnd);
	
	return new Rect2(start, Mathf.Abs(end.X - start.X), Mathf.Abs(end.Y - start.Y));
}
public override void _Process(double delta) //in a NinePatchRect control
{
	base._Process(delta);

	Rect2 r = GetScreenRect(unit.Mesh);
	Position = r.Position + r.Size * Vector2.Up;
	Size = r.Size;
}

boundin box

1 Like

I am no expert, but I womder if this would be better to do with a shader?

It might be doable with a shader, but I ideally want this info in main memory and it would certainly be wasteful to calculate it in a shader and then retrieve the info. (plus then I’d have to learn how Godot handles shaders which is on my todo list but pretty far down at the moment haha)

I am using it to spawn Controls over the models so I can keyboard/gamepad navigate to them. So having it in main memory is important

If there’s a direct way of making a Node3D keyboard/gamepad navigable it would sidestep the whole issue, but I cant find one, so placing a control on top of them seems the simplest way of achieving that goal

1 Like

I figured it out by calculating the corners of the Aabb and then calculating the screen-space rect from the min and max of those.

Aabb box = mesh.GetAabb();
Vector3 offset = (mesh.GetParent() as Node3D).ToGlobal(mesh.Position);

box.Position = box.Position + offset; 

Vector3[] verts = new Vector3[8];
verts[0] = box.Position;
verts[1] = new Vector3(box.Position.X + box.Size.X, box.Position.Y, box.Position.Z);
verts[2] = new Vector3(box.Position.X + box.Size.X, box.Position.Y, box.Position.Z + box.Size.Z);
verts[3] = new Vector3(box.Position.X, box.Position.Y, box.Position.Z + box.Size.Z);

verts[4] = new Vector3(box.Position.X, box.Position.Y + box.Size.Y, box.Position.Z);
verts[5] = new Vector3(box.Position.X + box.Size.X, box.Position.Y + box.Size.Y, box.Position.Z);
verts[6] = new Vector3(box.Position.X + box.Size.X, box.Position.Y + box.Size.Y, box.Position.Z + box.Size.Z);
verts[7] = new Vector3(box.Position.X, box.Position.Y + box.Size.Y, box.Position.Z + box.Size.Z);

Vector2 controlStart = new Vector2(float.MaxValue, float.MaxValue);
Vector2 controlEnd = new Vector2(float.MinValue, float.MinValue);


for (int iii = 0; iii < cornerVerts.Length; iii++)
{
	Vector2 screenVert = Global.CurrentEnvironment.MainCamera.UnprojectPosition(verts[iii]);

	if (screenVert.X < controlStart.X)
		controlStart.X = screenVert.X;
	if (screenVert.Y < controlStart.Y)
		controlStart.Y = screenVert.Y;

	if (screenVert.X > controlEnd.X)
		controlEnd.X = screenVert.X;
	if (screenVert.Y > controlEnd.Y)
		controlEnd.Y = screenVert.Y;
}

Position = controlStart;
CustomMinimumSize = controlEnd - controlStart;
2 Likes

You’re an allstar for coming back with the answer. I played with it some but didnt get it.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.