Informatica

Deformatore Mesh in C#

copertinaMeshDeform

Studio di classi che consentono la modifica dei vertici una mesh a run time utile nei casi in cui un'oggetto 3D come ad esempio un veicolo deve simulare la deformazione della sua carrozzeria a seguito di una collisione oppure un fluido deve simulare la deformazione della sua superficie a seguito del passaggio di un altro oggetto per poi ritornare nella forma iniziale.

Attendere il caricamento dell'applicazione 3D seguente e usare il mouse per modificare le superfici alla pressione del tasto SX su di esse.

In coda il codice sorgente delle classi scritte. 

 


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent (typeof(MeshFilter))]
[RequireComponent (typeof(MeshCollider))]

public class MeshDeformer : MonoBehaviour {

Mesh deformingMesh;
MeshCollider meshCollider;

Vector3[] originalVertices, displacedVertices;
//velocità di spostamento dei vertici
Vector3[] vertexVelocity;
public float springForce = 20f;
public float damping=5f;

private float uniformScale = 1f;
//if true return original form else no
public bool isElastic = true;

// Use this for initialization
void Start () {
deformingMesh = this.GetComponent<MeshFilter> ().mesh;
meshCollider = this.GetComponent<MeshCollider> ();
originalVertices = deformingMesh.vertices;
displacedVertices = new Vector3[originalVertices.Length];
for (int i = 0; i < displacedVertices.Length; i++) {
displacedVertices [i] = originalVertices [i];
}
vertexVelocity=new Vector3[displacedVertices.Length];
}

// Update is called once per frame
void Update () {
updateMeshVertex ();
}

public void AddDeformingForce(Vector3 puntoImpatto,float forza){
//il puntoImpatto ha coordinale globali devo convertirle nelle coordinate locali dell'oggetto
puntoImpatto=this.transform.InverseTransformPoint(puntoImpatto);

for (int i = 0; i < displacedVertices.Length; i++) {
this.addDeformingForceSingleVertex (i, puntoImpatto, forza);
}

Debug.DrawLine (Camera.main.transform.position, transform.InverseTransformPoint(puntoImpatto));
}

protected void addDeformingForceSingleVertex(int idVerticeCorrente, Vector3 puntoImpattoMesh, float forzaImpatto){
//calcolo il vettore tra il vertice della mesh e il punto di impatto quale differenza tra i due
Vector3 vettorePuntoToVertice = displacedVertices [idVerticeCorrente] - puntoImpattoMesh;
//scalo questo vettore sulla base di un eventuale ridimensionamento della mesh
vettorePuntoToVertice *= uniformScale;
//maggiore è la distanza tra il vertice e il punto di impatto minore è la forza di spostamento
float forzaAttenuata= forzaImpatto - (1f + vettorePuntoToVertice.sqrMagnitude);
float velocita = forzaAttenuata * Time.deltaTime;
//calcolo la velocità e la direzione di spostamento del vertice in base alle normali e conservo questa forza nel vettore
vertexVelocity[idVerticeCorrente]+=vettorePuntoToVertice.normalized*velocita;
}

/**Dal vettore delle velocità dei vertici sposto i vertici della mesh deformata*/
protected void updadeVertexPosition(int idVertice){
Vector3 velocita = this.vertexVelocity [idVertice];
Vector3 spostamento = displacedVertices [idVertice] - originalVertices [idVertice];
//lo springForce offre una sesistenza all'impatto riportando i vertici in posizione originale
velocita -= spostamento * springForce * Time.deltaTime;
//per prevenire una oscillazione eterna si forzano i vertici al damping nella posizione originale
velocita *= 1f - damping * Time.deltaTime;

vertexVelocity [idVertice] = velocita;
displacedVertices[idVertice]+= velocita * Time.deltaTime;
}

protected void updateMeshVertex(){
//uniformo la scala dell'oggetto in modo che oggetti ridimensionati funzionino in scala
uniformScale=transform.localScale.x;

for (int i = 0; i < displacedVertices.Length; i++) {
updadeVertexPosition (i);
}

//assegno i vertici deformati alla mesh
deformingMesh.vertices = displacedVertices;
deformingMesh.RecalculateNormals ();

//se la mesh non è elastica resta nella nuova forma quindi azzero il vettore velocita vertici e assegno la nuova forma diventa quella definitiva
if (!isElastic) {
originalVertices = displacedVertices;
vertexVelocity=new Vector3[displacedVertices.Length];
this.meshCollider.sharedMesh = deformingMesh;


}
}
}


 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MeshDeformerCollisionInput : MonoBehaviour {
//forza spostamento
public float force = 100.0f;

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

}

void OnCollisionEnter(Collision c){
Vector3 puntoMedioImpatto = Vector3.zero;
Vector3 puntoMedioNormal = Vector3.zero;

foreach (ContactPoint cp in c.contacts) {
Debug.DrawRay (cp.point,cp.normal,Color.white);
puntoMedioImpatto += cp.point;
puntoMedioNormal += cp.normal;

}


MeshDeformer meshDeformer=this.GetComponent<MeshDeformer>();
if (meshDeformer) {

meshDeformer.AddDeformingForce (puntoMedioImpatto, force);
Debug.DrawRay (puntoMedioImpatto,puntoMedioImpatto+c.relativeVelocity,Color.yellow);
}
}
}


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**questa classe implementa l'input da parte dell'utente per deformare una mesh
in alternativa invece dell'input utente si può implementare una classe che prende l'imput da una collisione
della mesh con un altra per deformarla.
Si può aggangiare questo script a una camera e usare il RayCasting per deformare una mesh in base all'input del mouse*/

public class MeshDeformerUserInput : MonoBehaviour {

//forza spostamento
public float force = 10.0f;
//scostamento spostamento
public float forceOffset=0.1f;

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
if (Input.GetMouseButton (0)) {
this.gestisciInput ();
}
}

public void gestisciInput(){
Ray raggioInput = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit oggettoColpito;
if(Physics.Raycast(raggioInput,out oggettoColpito)){
MeshDeformer meshDeformabile=oggettoColpito.collider.GetComponent<MeshDeformer>();
if(meshDeformabile!=null){
Vector3 point=oggettoColpito.point;
point += oggettoColpito.normal*forceOffset;
meshDeformabile.AddDeformingForce(point,force);
}else{
print(oggettoColpito.collider.name+" non ha uno script mesh deformabile\n");
}
}
}
}

 


© 2018 sito prototipale studio di GiuseppeGi