EditorSpawn.as 12 KB

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