Hello,
I am working on a program that automatically imports 3D models. I’ve been able to add components like box colliders and rigidbodies to these 3D models. I’ve written a script to auto-size a box collider to the size of a 3D object, and that works fine. However, the 3D objects are often off-center, which means that those transform arrows don’t come out of the center of the 3D object. That causes the box collider at center 0,0,0 to not enclose the 3D object properly. If the 3D object is off-center, is there a way I can write a script to automatically adjust the box collider’s center so that it aligns with the 3D objects? I attached some screenshots of the centered and off-centered box colliders for some context.
I would just make prefabs out of these things with an extra root GameObject so you can move the visible sub-part however you like. That’s pretty much The Unity Way™.
EDIT: just reading more you are loading these yourself? If so, then just add a constant offset to each vertex as you read it in. Or adjust them all post-reading. You’re welcome to see mesh manipulation examples in my MakeGeo project.
I’m a bit confused. You wrote a script that does already size the box collider? So why haven’t you centered it properly? Can you share your script?
Note that you currently have switched Unity’s gizmo mode to “center”. This can be misleading as the move gizmo would always be shown in the center of an object or the center of a group of selected objects. When you switch it to pivot, you’ll see where the actual origin of the object is. Your box collider is of course relative to that point.
The easiest way to determine the size and center of a box collider would be to use a Bounds struct, iterate through all vertices of your model and use Encapsulate on every vertex. The resulting bounds has a center and size which you could directly use for the BoxCollider. Both the collider and the vertices live in local space, so no conversion needed.
Yeah so here’s the script below. I have a parent empty game object that has the script “Open File” which imports the 3D object. The 3D object becomes a child. If you look in the picture below, the parent has all the components so that I don’t need to apply components to the child through a script.
While the parent has the box collider, the box collider needs to fit the child 3D object. So I was able to use the child’s mesh renderer to calculate the size of the object and transfer the size to the box collider. Now the box collider is the right size, but it is not aligned with the object (it’s close but not exact). It seems that calculating the center of the object the same way that I calculated the size of the object does not give the correct center, and the box collider is misaligned.
I did not write the DuplicateFaces function in this script, so I’m not well versed on how it works, but as far as I know it builds the 3D object’s mesh. Would I be able to modify that function to get the actual center of the 3D object?
If you’d like more information I’ll be happy to provide it. Thank you!
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using SFB;
using TMPro;
using Dummiesman;
using Valve.VR.InteractionSystem;
using Unity.XR.CoreUtils;
public class OpenFile : MonoBehaviour
{
public GameObject model;
public float scale;
private Vector3 size;
private Vector3 center;
public void OnClickOpen()
{
string[] paths = StandaloneFileBrowser.OpenFilePanel("Open File", "", "obj", false);
if (paths.Length > 0)
{
StartCoroutine(OutputRoutineOpen(new System.Uri(paths[0]).AbsoluteUri));
}
}
private void Update()
{
if (model != null)
{
model.transform.position = transform.position;
model.transform.rotation = transform.rotation;
}
}
private IEnumerator OutputRoutineOpen(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log("WWW ERROR: " + www.result);
}
else
{
MemoryStream textStream = new MemoryStream(Encoding.UTF8.GetBytes(www.downloadHandler.text));
if (model != null)
{
Destroy(model);
}
model = new OBJLoader().Load(textStream);
model.transform.localScale = new Vector3(-scale, scale, scale); //Set position of parent model and reverse X to show properly
model.transform.position = new Vector3(0, 1f, 0);
DuplicateFaces();
MeshRenderer render = model.GetComponentInChildren<MeshRenderer>();
size = render.bounds.size;
center = render.bounds.center;
BoxCollider boxCollider = GetComponent<BoxCollider>();
boxCollider.size = size;
boxCollider.center = center;
}
}
public void DuplicateFaces()
{
for (int i = 0; i < model.GetComponentsInChildren<Renderer>().Length; i++) //Loop through the model children
{
// Get original mesh components: vertices, normals triangles and texture coordinates
Mesh mesh = model.GetComponentsInChildren<MeshFilter>()[i].mesh;
Vector3[] vertices = mesh.vertices;
int numOfVertices = vertices.Length;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
int numOfTriangles = triangles.Length;
Vector2[] textureCoordinates = mesh.uv;
if (textureCoordinates.Length < numOfTriangles) //Check if mesh doesn't have texture coordinates
{
textureCoordinates = new Vector2[numOfVertices * 2];
}
// Create a new mesh component, double the size of the original
Vector3[] newVertices = new Vector3[numOfVertices * 2];
Vector3[] newNormals = new Vector3[numOfVertices * 2];
int[] newTriangle = new int[numOfTriangles * 2];
Vector2[] newTextureCoordinates = new Vector2[numOfVertices * 2];
for (int j = 0; j < numOfVertices; j++)
{
newVertices[j] = newVertices[j + numOfVertices] = vertices[j]; //Copy original vertices to make the second half of the mew vertices array
newTextureCoordinates[j] = newTextureCoordinates[j + numOfVertices] = textureCoordinates[j]; //Copy original texture coordinates to make the second half of the mew texture coordinates array
newNormals[j] = normals[j]; //First half of the new normals array is a copy original normals
newNormals[j + numOfVertices] = -normals[j]; //Second half of the new normals array reverse the original normals
}
for (int x = 0; x < numOfTriangles; x += 3)
{
// copy the original triangle for the first half of array
newTriangle[x] = triangles[x];
newTriangle[x + 1] = triangles[x + 1];
newTriangle[x + 2] = triangles[x + 2];
// Reversed triangles for the second half of array
int j = x + numOfTriangles;
newTriangle[j] = triangles[x] + numOfVertices;
newTriangle[j + 2] = triangles[x + 1] + numOfVertices;
newTriangle[j + 1] = triangles[x + 2] + numOfVertices;
}
mesh.vertices = newVertices;
mesh.uv = newTextureCoordinates;
mesh.normals = newNormals;
mesh.triangles = newTriangle;
}
}
}
So I’m looking through it and you’re right. The gizmo mode is center and the move gizmo is shown at the center of the object. When I change the gizmo mode to pivot, the move gizmo is shown at the center of the box collider.
When I print the center of the object’s mesh, it says 0, 0.96, 0.2. The box collider’s center is 0, 0, 0 by default. Changing the box collider’s center to the mesh’s center is not correct. The global transform for the object, its mesh, and its collider are all 0, 1, 0.
Can I somehow make the pivot point the center of the object? Can this be done automatically through a script so that I don’t have to manually adjust the box collider in the unity editor?
Thanks