- Home /
Breaking down Unity's Procedural Example "sculpt vertices"
Taken from the "sculpt vertices" of the Unity's "Procedural Examples":
function Update () {
// When no button is pressed we update the mesh collider
if (!Input.GetMouseButton (0))
{
// Apply collision mesh when we let go of button
ApplyMeshCollider();
return;
}
// Did we hit the surface?
var hit : RaycastHit;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast (ray, hit))
{
var filter : MeshFilter = hit.collider.GetComponent(MeshFilter);
if (filter)
{
// Don't update mesh collider every frame since physX
// does some heavy processing to optimize the collision mesh.
// So this is not fast enough for real time updating every frame
if (filter != unappliedMesh)
{
ApplyMeshCollider();
unappliedMesh = filter;
}
// Deform mesh
var relativePoint = filter.transform.InverseTransformPoint(hit.point);
DeformMesh(filter.mesh, relativePoint, pull * Time.deltaTime, radius);
}
}
}
function ApplyMeshCollider () {
if (unappliedMesh && unappliedMesh.GetComponent(MeshCollider)) {
unappliedMesh.GetComponent(MeshCollider).mesh = unappliedMesh.mesh;
}
unappliedMesh = null;
}
Could someone explain me the functionality of this piece of code? Especially the part of the mesh collider update. What exactly does and when? Which commands and under which sequence they function in order to hold it from not updating every frame?
I have examined the code thoroughly, but due to my minor programming level, I cannot understand some parts. Also by trying the example in run-time, I couldn't figure out the collider's behavior.
Thank you in advance Unity Community.
Answer by markpdolby · Nov 08, 2012 at 01:11 PM
// When no button is pressed we update the mesh collider
if (!Input.GetMouseButton (0))
{
// Apply collision mesh when we let go of button
ApplyMeshCollider();
return;
}
Here the mesh collider is updated when the user is not pressing the left mouse button, however the mesh collider is only likely to be updated once as the function ApplyMeshCollider will only update the collider if the unappliedMesh is not null and it is set to null at the end of the function everytime. So what this essentially means is when the user lets go of the left mouse button the collider is updated at most one time, and the return statement means the rest of the code is not reached. When the user does press the left mouse button this section of the code is called:
// Did we hit the surface?
var hit : RaycastHit;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast (ray, hit))
{
var filter : MeshFilter = hit.collider.GetComponent(MeshFilter);
if (filter)
{
// Don't update mesh collider every frame since physX
// does some heavy processing to optimize the collision mesh.
// So this is not fast enough for real time updating every frame
if (filter != unappliedMesh)
{
ApplyMeshCollider();
unappliedMesh = filter;
}
// Deform mesh
var relativePoint = filter.transform.InverseTransformPoint(hit.point);
DeformMesh(filter.mesh, relativePoint, pull * Time.deltaTime, radius);
}
}
This bit of code is doing the following; create a ray cast from the point of the mouse cursor (best way to imagine it is sending out a laser from your cursor which can hit objects in the scene), then check if the ray cast has hit anything in the scene, if it has hit something then try to get the mesh filter of that object, if the object does have a mesh filter then check if the two meshes are the same mesh (So if the deformed mesh you want to apply is the same as the current mesh then dont update the collider), if the two meshes are different then give the collider the new mesh and set the unapplied mesh to null.
I hope that clears a few things up for you.
EDIT: Ok so I managed to get the mesh collider to update properly, it probably isn't the base way to do it but hopefully you can build on it from there:
var CanUpdate = false;
function ApplyMeshCollider () {
if (unappliedMesh && unappliedMesh.GetComponent(MeshCollider)) {
if(CanUpdate)
{
unappliedMesh.GetComponent(MeshCollider).sharedMesh = null;
}
unappliedMesh.GetComponent(MeshCollider).sharedMesh = unappliedMesh.sharedMesh;
}
//unappliedMesh = null;
CanUpdate = !CanUpdate;
}
So try placing the variable at the top of the script with the rest and replacing the ApplyMeshCollider function with my one and see if that gives a better result.
That definitely clears more than a few things up! Thank you markpdolby for taking the time so soon to enlighten me. However, I apologize to say that I have some further questions (that probably exist because of my unawareness and not your explanations of course).
Therefore, I'd like to be more specific and also explain what I meant by "collider's behavior" at the first post, which I didn't want to overcrowd with text. So, and I don't know if you're familiar with the particular unity's example, here's what I see now: We have the mesh (this sphere in particular) and when we click on it, there's some deformation to the mesh. The collider is also updated (note: after this very first click) to "follow" the new mesh. I can see that by disabling the $$anonymous$$esh Renderer in the Inspector, so the $$anonymous$$esh Collider (green mesh) appears to agree with the deformed mesh. However, when I click again on the sphere, the mesh is deformed again, but now the Collider remains at the previous state (after the first deformation). I noticed that when I disable and then enable back the $$anonymous$$esh Collider (in the Inspector), the Collider takes the latest deformed mesh.
As far as I could explain the above, considering your explanations: At first the mesh filter is null and the Collider is not updated (the If() statement in the Apply$$anonymous$$eshCollider function is not fulfilled). From now on I am not sure if I follow well: Since we click on the mesh (translated of course that the ray cast "finds" the mesh), which is the same with the current (thus the Collider updating is skipped), the Deform$$anonymous$$esh function deforms it. While the Update function keeps executing and while the mouse button is still pressed, it is found that now the new mesh is different, but the unapplied$$anonymous$$esh is still null, so the collider is not updated, though the unapplied$$anonymous$$esh is set to the new mesh. When the mouse button is released, however, the unapplied$$anonymous$$esh is not null anymore and the collider is updated (the If() statement in the Apply$$anonymous$$eshCollider function is now fulfilled).
But then I don't understand why the Apply$$anonymous$$eshCollider function is called when the mouse button is pressed, since the collider is not updated (because the unapplied$$anonymous$$esh will be set to null). Also, why the collider doesn't seem to get updated a second time (after the mesh has been deformed again), so that someone could manipulate the already displaced vertices again. And how's that related to the fact that the Collider is updated, when I dis/enable the component in the Inspector.
I would really need this extra information, though I don't see why your answer wouldn't be already marked as correct. $$anonymous$$oreover, sorry that took me so much time to comment back, but I was trying to realise what I have understood anymore, after your answer. Thank you again! And I hope I have not confused you.
I have been looking into the scene myself and I am having the same problems as you are, I get the feeling that it might of worked for the Unity version at the time the tutorial was released but something in the recent version has broken it. The reason I believe this is the case is because for the $$anonymous$$eshCollider they use .mesh which has been depreciated for .shared$$anonymous$$esh (http://docs.unity3d.com/Documentation/ScriptReference/$$anonymous$$eshCollider-shared$$anonymous$$esh.html) I will have a look at the code and see if there is a way to update the mesh collider properly in the current version of unity.
Updated my answer with a potential fix for updating the mesh collider properly.
Oh, thank you that's great! I will make some tests with your new code during the day (or tomorrow). Then I'll come back with my findings. So far I just dropped two lines disabling and enabling the Collider component, but this gives a pause, until the new collider is built, for meshes with quite a lot vertices. But your contribution seems more sophisticated. I really appreciate it that you took the time to look into it and answer back!
EDIT: I hate markpdolby to disturb you again, but could you please explain me the workflow/logic of your new suggestion on the Apply$$anonymous$$eshCollider function (line by line if possible...)? I couldn't figure out myself no matter how much I tried...
function Apply$$anonymous$$eshCollider () {
if (unapplied$$anonymous$$esh && unapplied$$anonymous$$esh.GetComponent($$anonymous$$eshCollider)) {
unapplied$$anonymous$$esh.GetComponent($$anonymous$$eshCollider).shared$$anonymous$$esh = null;
unapplied$$anonymous$$esh.GetComponent($$anonymous$$eshCollider).shared$$anonymous$$esh = unapplied$$anonymous$$esh.shared$$anonymous$$esh;
}
}
Turns out that you dont actually need the CanUpdate part, so what I am doing here is essentially mimicking the turning on and off of the mesh collier in the editor which was giving the correct collider outline. Hope that clears it up a bit.