EditorSpawn.as 15 KB

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