123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- #if UNITY_STANDALONE || UNITY_EDITOR
-
- using UnityEngine;
- using System.Collections;
- using ProBuilder2.Common;
-
- namespace ProBuilder2.Examples
- {
-
- /**
- * \brief This class allows the user to select a single face at a time and move it forwards or backwards.
- * More advanced usage of the ProBuilder API should make use of the pb_Object->SelectedFaces list to keep
- * track of the selected faces.
- */
- public class RuntimeEdit : MonoBehaviour
- {
- class pb_Selection
- {
- public pb_Object pb; ///< This is the currently selected ProBuilder object.
- public pb_Face face; ///< Keep a reference to the currently selected face.
-
- public pb_Selection(pb_Object _pb, pb_Face _face)
- {
- pb = _pb;
- face = _face;
- }
-
- public bool HasObject()
- {
- return pb != null;
- }
-
- public bool IsValid()
- {
- return pb != null && face != null;
- }
-
- public bool Equals(pb_Selection sel)
- {
- if(sel != null && sel.IsValid())
- return (pb == sel.pb && face == sel.face);
- else
- return false;
- }
-
- public void Destroy()
- {
- if(pb != null)
- GameObject.Destroy(pb.gameObject);
- }
-
- public override string ToString()
- {
- return "pb_Object: " + pb == null ? "Null" : pb.name +
- "\npb_Face: " + ( (face == null) ? "Null" : face.ToString() );
- }
- }
-
- pb_Selection currentSelection;
- pb_Selection previousSelection;
-
- private pb_Object preview;
- public Material previewMaterial;
-
- /**
- * \brief Wake up!
- */
- void Awake()
- {
- SpawnCube();
- }
-
- /**
- * \brief This is the usual Unity OnGUI method. We only use it to show a 'Reset' button.
- */
- void OnGUI()
- {
- // To reset, nuke the pb_Object and build a new one.
- if(GUI.Button(new Rect(5, Screen.height - 25, 80, 20), "Reset"))
- {
- currentSelection.Destroy();
- Destroy(preview.gameObject);
- SpawnCube();
- }
- }
-
- /**
- * \brief Creates a new ProBuilder cube and sets it up with a concave MeshCollider.
- */
- void SpawnCube()
- {
- // This creates a basic cube with ProBuilder features enabled. See the ProBuilder.Shape enum to
- // see all possible primitive types.
- pb_Object pb = pb_ShapeGenerator.CubeGenerator(Vector3.one);
-
- // The runtime component requires that a concave mesh collider be present in order for face selection
- // to work.
- pb.gameObject.AddComponent<MeshCollider>().convex = false;
-
- // Now set it to the currentSelection
- currentSelection = new pb_Selection(pb, null);
- }
-
- Vector2 mousePosition_initial = Vector2.zero;
- bool dragging = false;
- public float rotateSpeed = 100f;
-
- /**
- * \brief This is responsible for moving the camera around and not much else.
- */
- public void LateUpdate()
- {
- if(!currentSelection.HasObject())
- return;
-
- if(Input.GetMouseButtonDown(1) || (Input.GetMouseButtonDown(0) && Input.GetKey(KeyCode.LeftAlt)))
- {
- mousePosition_initial = Input.mousePosition;
- dragging = true;
- }
-
- if(dragging)
- {
- Vector2 delta = (Vector3)mousePosition_initial - (Vector3)Input.mousePosition;
- Vector3 dir = new Vector3(delta.y, delta.x, 0f);
-
- currentSelection.pb.gameObject.transform.RotateAround(Vector3.zero, dir, rotateSpeed * Time.deltaTime);
-
- // If there is a currently selected face, update the preview.
- if(currentSelection.IsValid())
- RefreshSelectedFacePreview();
- }
-
- if(Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(0))
- {
- dragging = false;
- }
- }
-
- /**
- * \brief The 'meat' of the operation. This listens for a click event, then checks for a positive
- * face selection. If the click has hit a pb_Object, select it.
- */
- public void Update()
- {
- if(Input.GetMouseButtonUp(0) && !Input.GetKey(KeyCode.LeftAlt)) {
-
- if(FaceCheck(Input.mousePosition))
- {
- if(currentSelection.IsValid())
- {
- // Check if this face has been previously selected, and if so, move the face.
- // Otherwise, just accept this click as a selection.
- if(!currentSelection.Equals(previousSelection))
- {
- previousSelection = new pb_Selection(currentSelection.pb, currentSelection.face);
- RefreshSelectedFacePreview();
- return;
- }
-
- Vector3 localNormal = pb_Math.Normal( pbUtil.ValuesWithIndices(currentSelection.pb.vertices, currentSelection.face.distinctIndices) );
-
- if(Input.GetKey(KeyCode.LeftShift))
- currentSelection.pb.TranslateVertices( currentSelection.face.distinctIndices, localNormal.normalized * -.5f );
- else
- currentSelection.pb.TranslateVertices( currentSelection.face.distinctIndices, localNormal.normalized * .5f );
-
- // Refresh will update the Collision mesh volume, face UVs as applicatble, and normal information.
- currentSelection.pb.Refresh();
-
- // this create the selected face preview
- RefreshSelectedFacePreview();
- }
- }
- }
- }
-
- /**
- * \brief This is how we figure out what face is clicked.
- */
- public bool FaceCheck(Vector3 pos)
- {
- Ray ray = Camera.main.ScreenPointToRay (pos);
- RaycastHit hit;
-
- if( Physics.Raycast(ray.origin, ray.direction, out hit))
- {
- pb_Object hitpb = hit.transform.gameObject.GetComponent<pb_Object>();
-
- if(hitpb == null)
- return false;
-
- Mesh m = hitpb.msh;
-
- int[] tri = new int[3] {
- m.triangles[hit.triangleIndex * 3 + 0],
- m.triangles[hit.triangleIndex * 3 + 1],
- m.triangles[hit.triangleIndex * 3 + 2]
- };
-
- currentSelection.pb = hitpb;
-
- return hitpb.FaceWithTriangle(tri, out currentSelection.face);
- }
- return false;
- }
-
- void RefreshSelectedFacePreview()
- {
- // Copy the currently selected vertices in world space.
- // World space so that we don't have to apply transforms
- // to match the current selection.
- Vector3[] verts = currentSelection.pb.VerticesInWorldSpace(currentSelection.face.indices);
-
- // face.indices == triangles, so wind the face to match
- int[] indices = new int[verts.Length];
- for(int i = 0; i < indices.Length; i++)
- indices[i] = i;
-
- // Now go through and move the verts we just grabbed out about .1m from the original face.
- Vector3 normal = pb_Math.Normal(verts);
-
- for(int i = 0; i < verts.Length; i++)
- verts[i] += normal.normalized * .01f;
-
- if(preview)
- Destroy(preview.gameObject);
-
- preview = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { new pb_Face(indices) });
- preview.SetFaceMaterial(preview.faces, previewMaterial);
- preview.ToMesh();
- preview.Refresh();
- }
- }
- }
- #endif
|