EditorSpawn.as 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // Urho3D spawn editor
  2. LineEdit@ randomRotationX;
  3. LineEdit@ randomRotationY;
  4. LineEdit@ randomRotationZ;
  5. LineEdit@ randomScaleMinEdit;
  6. LineEdit@ randomScaleMaxEdit;
  7. LineEdit@ numberSpawnedObjectsEdit;
  8. LineEdit@ spawnRadiusEdit;
  9. LineEdit@ spawnCountEdit;
  10. Window@ spawnWindow;
  11. Vector3 randomRotation = Vector3(0, 0, 0);
  12. float randomScaleMin = 1;
  13. float randomScaleMax = 1;
  14. uint spawnCount = 1;
  15. float spawnRadius = 0;
  16. bool useNormal = true;
  17. bool alignToAABBBottom = true;
  18. uint numberSpawnedObjects = 1;
  19. Array<String> spawnedObjectsNames;
  20. void CreateSpawnEditor()
  21. {
  22. if (spawnWindow !is null)
  23. return;
  24. spawnWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSpawnWindow.xml"));
  25. ui.root.AddChild(spawnWindow);
  26. spawnWindow.opacity = uiMaxOpacity;
  27. int height = Min(ui.root.height - 60, 500);
  28. spawnWindow.SetSize(300, height);
  29. CenterDialog(spawnWindow);
  30. HideSpawnEditor();
  31. SubscribeToEvent(spawnWindow.GetChild("CloseButton", true), "Released", "HideSpawnEditor");
  32. randomRotationX = spawnWindow.GetChild("RandomRotation.x", true);
  33. randomRotationY = spawnWindow.GetChild("RandomRotation.y", true);
  34. randomRotationZ = spawnWindow.GetChild("RandomRotation.z", true);
  35. randomRotationX.text = String(randomRotation.x);
  36. randomRotationY.text = String(randomRotation.y);
  37. randomRotationZ.text = String(randomRotation.z);
  38. randomScaleMinEdit = spawnWindow.GetChild("RandomScaleMin", true);
  39. randomScaleMaxEdit = spawnWindow.GetChild("RandomScaleMax", true);
  40. randomScaleMinEdit.text = String(randomScaleMin);
  41. randomScaleMaxEdit.text = String(randomScaleMax);
  42. CheckBox@ useNormalToggle = spawnWindow.GetChild("UseNormal", true);
  43. useNormalToggle.checked = useNormal;
  44. CheckBox@ alignToAABBBottomToggle = spawnWindow.GetChild("AlignToAABBBottom", true);
  45. alignToAABBBottomToggle.checked = alignToAABBBottom;
  46. numberSpawnedObjectsEdit = spawnWindow.GetChild("NumberSpawnedObjects", true);
  47. numberSpawnedObjectsEdit.text = String(numberSpawnedObjects);
  48. spawnRadiusEdit = spawnWindow.GetChild("SpawnRadius", true);
  49. spawnCountEdit = spawnWindow.GetChild("SpawnCount", true);
  50. spawnRadiusEdit.text = String(spawnRadius);
  51. spawnCountEdit.text = String(spawnCount);
  52. SubscribeToEvent(randomRotationX, "TextChanged", "EditRandomRotation");
  53. SubscribeToEvent(randomRotationY, "TextChanged", "EditRandomRotation");
  54. SubscribeToEvent(randomRotationZ, "TextChanged", "EditRandomRotation");
  55. SubscribeToEvent(randomScaleMinEdit, "TextChanged", "EditRandomScale");
  56. SubscribeToEvent(randomScaleMaxEdit, "TextChanged", "EditRandomScale");
  57. SubscribeToEvent(spawnRadiusEdit, "TextChanged", "EditSpawnRadius");
  58. SubscribeToEvent(spawnCountEdit, "TextChanged", "EditSpawnCount");
  59. SubscribeToEvent(useNormalToggle, "Toggled", "ToggleUseNormal");
  60. SubscribeToEvent(alignToAABBBottomToggle, "Toggled", "ToggleAlignToAABBBottom");
  61. SubscribeToEvent(numberSpawnedObjectsEdit, "TextFinished", "UpdateNumberSpawnedObjects");
  62. SubscribeToEvent(spawnWindow.GetChild("SetSpawnMode", true), "Released", "SetSpawnMode");
  63. RefreshPickedObjects();
  64. }
  65. bool ShowSpawnEditor()
  66. {
  67. spawnWindow.visible = true;
  68. spawnWindow.BringToFront();
  69. return true;
  70. }
  71. void HideSpawnEditor()
  72. {
  73. spawnWindow.visible = false;
  74. }
  75. void PickSpawnObject()
  76. {
  77. @resourcePicker = GetResourcePicker(StringHash("Node"));
  78. if (resourcePicker is null)
  79. return;
  80. String lastPath = resourcePicker.lastPath;
  81. if (lastPath.empty)
  82. lastPath = sceneResourcePath;
  83. CreateFileSelector("Pick " + resourcePicker.typeName, "OK", "Cancel", lastPath, resourcePicker.filters, resourcePicker.lastFilter);
  84. SubscribeToEvent(uiFileSelector, "FileSelected", "PickSpawnObjectDone");
  85. }
  86. void EditRandomRotation(StringHash eventType, VariantMap& eventData)
  87. {
  88. LineEdit@ edit = eventData["Element"].GetPtr();
  89. randomRotation = Vector3(randomRotationX.text.ToFloat(), randomRotationY.text.ToFloat(), randomRotationZ.text.ToFloat());
  90. UpdateHierarchyItem(editorScene);
  91. }
  92. void EditRandomScale(StringHash eventType, VariantMap& eventData)
  93. {
  94. LineEdit@ edit = eventData["Element"].GetPtr();
  95. randomScaleMin = randomScaleMinEdit.text.ToFloat();
  96. randomScaleMax = randomScaleMaxEdit.text.ToFloat();
  97. UpdateHierarchyItem(editorScene);
  98. }
  99. void ToggleUseNormal(StringHash eventType, VariantMap& eventData)
  100. {
  101. useNormal = cast<CheckBox>(eventData["Element"].GetPtr()).checked;
  102. }
  103. void ToggleAlignToAABBBottom(StringHash eventType, VariantMap& eventData)
  104. {
  105. alignToAABBBottom = cast<CheckBox>(eventData["Element"].GetPtr()).checked;
  106. }
  107. void UpdateNumberSpawnedObjects(StringHash eventType, VariantMap& eventData)
  108. {
  109. LineEdit@ edit = eventData["Element"].GetPtr();
  110. numberSpawnedObjects = edit.text.ToUInt();
  111. edit.text = String(numberSpawnedObjects);
  112. RefreshPickedObjects();
  113. }
  114. void EditSpawnRadius(StringHash eventType, VariantMap& eventData)
  115. {
  116. LineEdit@ edit = eventData["Element"].GetPtr();
  117. spawnRadius = edit.text.ToFloat();
  118. }
  119. void EditSpawnCount(StringHash eventType, VariantMap& eventData)
  120. {
  121. LineEdit@ edit = eventData["Element"].GetPtr();
  122. spawnCount = edit.text.ToUInt();
  123. }
  124. void RefreshPickedObjects()
  125. {
  126. spawnedObjectsNames.Resize(numberSpawnedObjects);
  127. ListView@ list = spawnWindow.GetChild("SpawnedObjects", true);
  128. list.RemoveAllItems();
  129. for (uint i = 0; i < numberSpawnedObjects; ++i)
  130. {
  131. UIElement@ parent = CreateAttributeEditorParentWithSeparatedLabel(list, "Object " +(i+1), i, 0, false);
  132. UIElement@ container = UIElement();
  133. container.SetLayout(LM_HORIZONTAL, 4, IntRect(10, 0, 4, 0));
  134. container.SetFixedHeight(ATTR_HEIGHT);
  135. parent.AddChild(container);
  136. LineEdit@ nameEdit = CreateAttributeLineEdit(container, null, i, 0);
  137. nameEdit.name = "TextureNameEdit" + String(i);
  138. Button@ pickButton = CreateResourcePickerButton(container, null, i, 0, "Pick");
  139. SubscribeToEvent(pickButton, "Released", "PickSpawnedObject");
  140. nameEdit.text = spawnedObjectsNames[i];
  141. SubscribeToEvent(nameEdit, "TextFinished", "EditSpawnedObjectName");
  142. }
  143. }
  144. void EditSpawnedObjectName(StringHash eventType, VariantMap& eventData)
  145. {
  146. LineEdit@ nameEdit = eventData["Element"].GetPtr();
  147. int index = nameEdit.vars["Index"].GetUInt();
  148. String resourceName = VerifySpawnedObjectFile(nameEdit.text);
  149. nameEdit.text = resourceName;
  150. spawnedObjectsNames[index] = resourceName;
  151. }
  152. String VerifySpawnedObjectFile(const String&in resourceName)
  153. {
  154. File@ file = cache.GetFile(resourceName);
  155. if(file !is null)
  156. return resourceName;
  157. else
  158. return String();
  159. }
  160. void PickSpawnedObject(StringHash eventType, VariantMap& eventData)
  161. {
  162. UIElement@ button = eventData["Element"].GetPtr();
  163. resourcePickIndex = button.vars["Index"].GetUInt();
  164. CreateFileSelector("Pick spawned object", "Pick", "Cancel", uiNodePath, uiSceneFilters, uiNodeFilter);
  165. SubscribeToEvent(uiFileSelector, "FileSelected", "PickSpawnedObjectNameDone");
  166. }
  167. void PickSpawnedObjectNameDone(StringHash eventType, VariantMap& eventData)
  168. {
  169. StoreResourcePickerPath();
  170. CloseFileSelector();
  171. if (!eventData["OK"].GetBool())
  172. {
  173. @resourcePicker = null;
  174. return;
  175. }
  176. String resourceName = GetResourceNameFromFullName(eventData["FileName"].GetString());
  177. spawnedObjectsNames[resourcePickIndex] = VerifySpawnedObjectFile(resourceName);
  178. @resourcePicker = null;
  179. RefreshPickedObjects();
  180. }
  181. void SetSpawnMode(StringHash eventType, VariantMap& eventData)
  182. {
  183. editMode = EDIT_SPAWN;
  184. }
  185. void PlaceObject(Vector3 spawnPosition, Vector3 normal)
  186. {
  187. Quaternion spawnRotation;
  188. if (useNormal)
  189. spawnRotation = Quaternion(Vector3(0, 1, 0), normal);
  190. spawnRotation = Quaternion(Random(-randomRotation.x, randomRotation.x),
  191. Random(-randomRotation.y, randomRotation.y), Random(-randomRotation.z, randomRotation.z)) * spawnRotation;
  192. int number = RandomInt(0, spawnedObjectsNames.length);
  193. File@ file = cache.GetFile(spawnedObjectsNames[number]);
  194. Node@ spawnedObject = InstantiateNodeFromFile(file, spawnPosition, spawnRotation, Random(randomScaleMin, randomScaleMax));
  195. if (spawnedObject is null)
  196. {
  197. spawnedObjectsNames[number] = spawnedObjectsNames[spawnedObjectsNames.length - 1];
  198. --numberSpawnedObjects;
  199. RefreshPickedObjects();
  200. return;
  201. }
  202. }
  203. bool GetSpawnPosition(const Ray&in cameraRay, float maxDistance, Vector3&out position, Vector3&out normal, float randomRadius = 0.0,
  204. bool allowNoHit = true)
  205. {
  206. if (pickMode < PICK_RIGIDBODIES && editorScene.octree !is null)
  207. {
  208. RayQueryResult result = editorScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, maxDistance, DRAWABLE_GEOMETRY,
  209. 0x7fffffff);
  210. if (result.drawable !is null)
  211. {
  212. if (randomRadius > 0)
  213. {
  214. Vector3 basePosition = RandomizeSpawnPosition(result.position, randomRadius);
  215. basePosition.y += randomRadius;
  216. result = editorScene.octree.RaycastSingle(Ray(basePosition, Vector3(0, -1, 0)), RAY_TRIANGLE, randomRadius * 2.0,
  217. DRAWABLE_GEOMETRY, 0x7fffffff);
  218. if (result.drawable !is null)
  219. {
  220. position = result.position;
  221. normal = result.normal;
  222. return true;
  223. }
  224. }
  225. else
  226. {
  227. position = result.position;
  228. normal = result.normal;
  229. return true;
  230. }
  231. }
  232. }
  233. else if (editorScene.physicsWorld !is null)
  234. {
  235. // If we are not running the actual physics update, refresh collisions before raycasting
  236. if (!runUpdate)
  237. editorScene.physicsWorld.UpdateCollisions();
  238. PhysicsRaycastResult result = editorScene.physicsWorld.RaycastSingle(cameraRay, maxDistance);
  239. if (result.body !is null)
  240. {
  241. if (randomRadius > 0)
  242. {
  243. Vector3 basePosition = RandomizeSpawnPosition(result.position, randomRadius);
  244. basePosition.y += randomRadius;
  245. result = editorScene.physicsWorld.RaycastSingle(Ray(basePosition, Vector3(0, -1, 0)), randomRadius * 2.0);
  246. if (result.body !is null)
  247. {
  248. position = result.position;
  249. normal = result.normal;
  250. return true;
  251. }
  252. }
  253. else
  254. {
  255. position = result.position;
  256. normal = result.normal;
  257. return true;
  258. }
  259. }
  260. }
  261. position = cameraRay.origin + cameraRay.direction * maxDistance;
  262. normal = Vector3(0, 1, 0);
  263. return allowNoHit;
  264. }
  265. Vector3 RandomizeSpawnPosition(const Vector3&in position, float randomRadius)
  266. {
  267. float angle = Random() * 360.0;
  268. float distance = Random() * randomRadius;
  269. return position + Quaternion(0, angle, 0) * Vector3(0, 0, distance);
  270. }
  271. void SpawnObject()
  272. {
  273. if (spawnedObjectsNames.length == 0)
  274. return;
  275. IntRect view = activeViewport.viewport.rect;
  276. for (uint i = 0; i < spawnCount; ++i)
  277. {
  278. Ray cameraRay = GetActiveViewportCameraRay();
  279. Vector3 position, normal;
  280. if (GetSpawnPosition(cameraRay, camera.farClip, position, normal, spawnRadius, false))
  281. PlaceObject(position, normal);
  282. }
  283. }