The Swartz-Manning’s first exhibit will provide a detailed history of Aaron Swartz Day. https://www.aaronswartzday.org/vr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RuntimeEdit.cs 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #if UNITY_STANDALONE || UNITY_EDITOR
  2. using UnityEngine;
  3. using System.Collections;
  4. using ProBuilder2.Common;
  5. namespace ProBuilder2.Examples
  6. {
  7. /**
  8. * \brief This class allows the user to select a single face at a time and move it forwards or backwards.
  9. * More advanced usage of the ProBuilder API should make use of the pb_Object->SelectedFaces list to keep
  10. * track of the selected faces.
  11. */
  12. public class RuntimeEdit : MonoBehaviour
  13. {
  14. class pb_Selection
  15. {
  16. public pb_Object pb; ///< This is the currently selected ProBuilder object.
  17. public pb_Face face; ///< Keep a reference to the currently selected face.
  18. public pb_Selection(pb_Object _pb, pb_Face _face)
  19. {
  20. pb = _pb;
  21. face = _face;
  22. }
  23. public bool HasObject()
  24. {
  25. return pb != null;
  26. }
  27. public bool IsValid()
  28. {
  29. return pb != null && face != null;
  30. }
  31. public bool Equals(pb_Selection sel)
  32. {
  33. if(sel != null && sel.IsValid())
  34. return (pb == sel.pb && face == sel.face);
  35. else
  36. return false;
  37. }
  38. public void Destroy()
  39. {
  40. if(pb != null)
  41. GameObject.Destroy(pb.gameObject);
  42. }
  43. public override string ToString()
  44. {
  45. return "pb_Object: " + pb == null ? "Null" : pb.name +
  46. "\npb_Face: " + ( (face == null) ? "Null" : face.ToString() );
  47. }
  48. }
  49. pb_Selection currentSelection;
  50. pb_Selection previousSelection;
  51. private pb_Object preview;
  52. public Material previewMaterial;
  53. /**
  54. * \brief Wake up!
  55. */
  56. void Awake()
  57. {
  58. SpawnCube();
  59. }
  60. /**
  61. * \brief This is the usual Unity OnGUI method. We only use it to show a 'Reset' button.
  62. */
  63. void OnGUI()
  64. {
  65. // To reset, nuke the pb_Object and build a new one.
  66. if(GUI.Button(new Rect(5, Screen.height - 25, 80, 20), "Reset"))
  67. {
  68. currentSelection.Destroy();
  69. Destroy(preview.gameObject);
  70. SpawnCube();
  71. }
  72. }
  73. /**
  74. * \brief Creates a new ProBuilder cube and sets it up with a concave MeshCollider.
  75. */
  76. void SpawnCube()
  77. {
  78. // This creates a basic cube with ProBuilder features enabled. See the ProBuilder.Shape enum to
  79. // see all possible primitive types.
  80. pb_Object pb = pb_ShapeGenerator.CubeGenerator(Vector3.one);
  81. // The runtime component requires that a concave mesh collider be present in order for face selection
  82. // to work.
  83. pb.gameObject.AddComponent<MeshCollider>().convex = false;
  84. // Now set it to the currentSelection
  85. currentSelection = new pb_Selection(pb, null);
  86. }
  87. Vector2 mousePosition_initial = Vector2.zero;
  88. bool dragging = false;
  89. public float rotateSpeed = 100f;
  90. /**
  91. * \brief This is responsible for moving the camera around and not much else.
  92. */
  93. public void LateUpdate()
  94. {
  95. if(!currentSelection.HasObject())
  96. return;
  97. if(Input.GetMouseButtonDown(1) || (Input.GetMouseButtonDown(0) && Input.GetKey(KeyCode.LeftAlt)))
  98. {
  99. mousePosition_initial = Input.mousePosition;
  100. dragging = true;
  101. }
  102. if(dragging)
  103. {
  104. Vector2 delta = (Vector3)mousePosition_initial - (Vector3)Input.mousePosition;
  105. Vector3 dir = new Vector3(delta.y, delta.x, 0f);
  106. currentSelection.pb.gameObject.transform.RotateAround(Vector3.zero, dir, rotateSpeed * Time.deltaTime);
  107. // If there is a currently selected face, update the preview.
  108. if(currentSelection.IsValid())
  109. RefreshSelectedFacePreview();
  110. }
  111. if(Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(0))
  112. {
  113. dragging = false;
  114. }
  115. }
  116. /**
  117. * \brief The 'meat' of the operation. This listens for a click event, then checks for a positive
  118. * face selection. If the click has hit a pb_Object, select it.
  119. */
  120. public void Update()
  121. {
  122. if(Input.GetMouseButtonUp(0) && !Input.GetKey(KeyCode.LeftAlt)) {
  123. if(FaceCheck(Input.mousePosition))
  124. {
  125. if(currentSelection.IsValid())
  126. {
  127. // Check if this face has been previously selected, and if so, move the face.
  128. // Otherwise, just accept this click as a selection.
  129. if(!currentSelection.Equals(previousSelection))
  130. {
  131. previousSelection = new pb_Selection(currentSelection.pb, currentSelection.face);
  132. RefreshSelectedFacePreview();
  133. return;
  134. }
  135. Vector3 localNormal = pb_Math.Normal( pbUtil.ValuesWithIndices(currentSelection.pb.vertices, currentSelection.face.distinctIndices) );
  136. if(Input.GetKey(KeyCode.LeftShift))
  137. currentSelection.pb.TranslateVertices( currentSelection.face.distinctIndices, localNormal.normalized * -.5f );
  138. else
  139. currentSelection.pb.TranslateVertices( currentSelection.face.distinctIndices, localNormal.normalized * .5f );
  140. // Refresh will update the Collision mesh volume, face UVs as applicatble, and normal information.
  141. currentSelection.pb.Refresh();
  142. // this create the selected face preview
  143. RefreshSelectedFacePreview();
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * \brief This is how we figure out what face is clicked.
  150. */
  151. public bool FaceCheck(Vector3 pos)
  152. {
  153. Ray ray = Camera.main.ScreenPointToRay (pos);
  154. RaycastHit hit;
  155. if( Physics.Raycast(ray.origin, ray.direction, out hit))
  156. {
  157. pb_Object hitpb = hit.transform.gameObject.GetComponent<pb_Object>();
  158. if(hitpb == null)
  159. return false;
  160. Mesh m = hitpb.msh;
  161. int[] tri = new int[3] {
  162. m.triangles[hit.triangleIndex * 3 + 0],
  163. m.triangles[hit.triangleIndex * 3 + 1],
  164. m.triangles[hit.triangleIndex * 3 + 2]
  165. };
  166. currentSelection.pb = hitpb;
  167. return hitpb.FaceWithTriangle(tri, out currentSelection.face);
  168. }
  169. return false;
  170. }
  171. void RefreshSelectedFacePreview()
  172. {
  173. // Copy the currently selected vertices in world space.
  174. // World space so that we don't have to apply transforms
  175. // to match the current selection.
  176. Vector3[] verts = currentSelection.pb.VerticesInWorldSpace(currentSelection.face.indices);
  177. // face.indices == triangles, so wind the face to match
  178. int[] indices = new int[verts.Length];
  179. for(int i = 0; i < indices.Length; i++)
  180. indices[i] = i;
  181. // Now go through and move the verts we just grabbed out about .1m from the original face.
  182. Vector3 normal = pb_Math.Normal(verts);
  183. for(int i = 0; i < verts.Length; i++)
  184. verts[i] += normal.normalized * .01f;
  185. if(preview)
  186. Destroy(preview.gameObject);
  187. preview = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[] { new pb_Face(indices) });
  188. preview.SetFaceMaterial(preview.faces, previewMaterial);
  189. preview.ToMesh();
  190. preview.Refresh();
  191. }
  192. }
  193. }
  194. #endif