TDME2  1.9.200
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
TerrainEditorTabController.cpp
Go to the documentation of this file.
2 
3 #include <array>
4 #include <memory>
5 #include <string>
6 #include <unordered_map>
7 #include <vector>
8 
9 #include <tdme/tdme.h>
11 #include <tdme/engine/Texture.h>
15 #include <tdme/engine/Engine.h>
20 #include <tdme/gui/nodes/GUINode.h>
25 #include <tdme/gui/GUI.h>
26 #include <tdme/gui/GUIParser.h>
40 #include <tdme/utilities/Action.h>
41 #include <tdme/utilities/Console.h>
44 #include <tdme/utilities/Float.h>
45 #include <tdme/utilities/Integer.h>
48 #include <tdme/utilities/Terrain.h>
49 
50 #include <ext/tinyxml/tinyxml.h>
51 
53 
54 using std::array;
55 using std::make_unique;
56 using std::string;
57 using std::unique_ptr;
58 using std::unordered_map;
59 using std::vector;
60 
75 using tdme::gui::GUI;
99 
103 
104 #define AVOID_NULLPTR_STRING(arg) (arg == nullptr?"":arg)
105 
106 TerrainEditorTabController::TerrainEditorTabController(TerrainEditorTabView* view)
107 {
108  this->view = view;
109  this->basePropertiesSubController = make_unique<BasePropertiesSubController>(view->getEditorView(), "terrain");
110  this->popUps = view->getPopUps();
111  this->currentTerrainBrushTextureFileName = "resources/engine/textures/terrain_brush_soft.png";
112  this->currentTerrainBrushTexture = TextureReader::read(Tools::getPathName(currentTerrainBrushTextureFileName), Tools::getFileName(currentTerrainBrushTextureFileName), false, false);
113  this->rampTerrainBrushTexture = TextureReader::read("./resources/engine/textures", "terrain_ramp.png", false, false);
114 }
115 
117 }
118 
120 {
121  this->screenNode = screenNode;
124 }
125 
127 {
128 }
129 
131 {
132  switch (command) {
133  case COMMAND_SAVE:
134  {
135  //
136  auto prototype = view->getPrototype();
137  if (prototype == nullptr) return;
138 
139  //
140  try {
141  if (prototype->getFileName().empty() == true) throw ExceptionBase("Could not save file. No filename known");
142  view->saveFile(
143  Tools::getPathName(prototype->getFileName()),
144  Tools::getFileName(prototype->getFileName())
145  );
146  } catch (Exception& exception) {
147  showInfoPopUp("Warning", string(exception.what()));
148  }
149  }
150  break;
151  case COMMAND_SAVEAS:
152  {
153  class OnModelSave: public virtual Action
154  {
155  public:
156  void performAction() override {
157  try {
158  modelEditorTabController->view->saveFile(
159  modelEditorTabController->popUps->getFileDialogScreenController()->getPathName(),
160  modelEditorTabController->popUps->getFileDialogScreenController()->getFileName()
161  );
162  } catch (Exception& exception) {
163  modelEditorTabController->showInfoPopUp("Warning", string(exception.what()));
164  }
165  modelEditorTabController->popUps->getFileDialogScreenController()->close();
166  }
167 
168  /**
169  * Public constructor
170  * @param modelEditorTabController model editor tab controller
171  */
172  OnModelSave(TerrainEditorTabController* modelEditorTabController): modelEditorTabController(modelEditorTabController) {
173  }
174 
175  private:
176  TerrainEditorTabController* modelEditorTabController;
177  };
178 
179  //
180  auto prototype = view->getPrototype();
181  if (prototype == nullptr) return;
182 
183  //
185  prototype->getFileName().empty() == false?Tools::getPathName(prototype->getFileName()):string(),
186  "Save to: ",
187  {{ "tterrain" }},
188  Tools::getFileName(prototype->getFileName()),
189  false,
190  new OnModelSave(this)
191  );
192  }
193  break;
194  default:
195  showInfoPopUp("Warning", "This command is not supported yet");
196  break;
197  }
198 }
199 
200 void TerrainEditorTabController::onDrop(const string& payload, int mouseX, int mouseY) {
201  if (StringTools::startsWith(payload, "file:") == false) {
202  showInfoPopUp("Warning", "Unknown payload in drop");
203  } else {
204  auto fileName = StringTools::substring(payload, string("file:").size());
205  if (view->getEditorView()->getScreenController()->isDropOnNode(mouseX, mouseY, "terrainbrush_texture") == true) {
206  if (Tools::hasFileExtension(fileName, TextureReader::getTextureExtensions()) == false) {
207  showInfoPopUp("Warning", "You can not drop this file here. Allowed file extensions are " + Tools::enumerateFileExtensions(TextureReader::getTextureExtensions()));
208  } else {
209  setTerrainBrushTexture(fileName);
210  }
211  } else
212  if (view->getEditorView()->getScreenController()->isDropOnNode(mouseX, mouseY, "foliagebrush_texture") == true) {
213  if (Tools::hasFileExtension(fileName, TextureReader::getTextureExtensions()) == false) {
214  showInfoPopUp("Warning", "You can not drop this file here. Allowed file extensions are " + Tools::enumerateFileExtensions(TextureReader::getTextureExtensions()));
215  } else {
216  setFoliageBrushTexture(fileName);
217  }
218  } else
219  if (view->getEditorView()->getScreenController()->isDropOnNode(mouseX, mouseY, "foliagebrush_prototype_file") == true) {
220  if (Tools::hasFileExtension(fileName, PrototypeReader::getModelExtensions()) == false) {
221  showInfoPopUp("Warning", "You can not drop this file here. Allowed file extensions are " + Tools::enumerateFileExtensions(PrototypeReader::getModelExtensions()));
222  } else {
223  setFoliageBrushPrototype(fileName);
224  }
225  } else {
226  showInfoPopUp("Warning", "You can not drop a file here");
227  }
228  }
229 }
230 
232  required_dynamic_cast<GUITextNode*>(screenNode->getNodeById(view->getTabId() + "_tab_text_info"))->setText(text);
233 }
234 
235 void TerrainEditorTabController::showInfoPopUp(const string& caption, const string& message)
236 {
237  popUps->getInfoDialogScreenController()->show(caption, message);
238 }
239 
241 {
242  if (basePropertiesSubController->onChange(node, view->getPrototype(), view->getPrototype()) == true) return;
243  //
244  if (node->getId() == "selectbox_outliner") {
245  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
246  updateDetails(outlinerNode);
247  } else
248  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_add") == true) {
249  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_ADD;
250  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
252  updateDetails("terrain.brush");
254  } else
255  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_substract") == true) {
256  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_SUBTRACT;
257  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
259  updateDetails("terrain.brush");
261  } else
262  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_flatten") == true) {
263  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_FLATTEN;
264  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
266  updateDetails("terrain.brush");
268  } else
269  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_smooth") == true) {
270  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_SMOOTH;
271  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
273  updateDetails("terrain.brush");
275  } else
276  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_ramp") == true) {
277  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_RAMP;
278  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
280  updateDetails("terrain.brush");
282  } else
283  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_terrain_delete") == true) {
284  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_DELETE;
285  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
287  updateDetails("terrain.brush");
289  } else
290  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_water_water") == true) {
291  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_WATER_ADD;
292  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
294  updateDetails("terrain.waters");
295  view->unsetBrush();
296  } else
297  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_water_delete") == true) {
298  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_WATER_DELETE;
299  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
301  updateDetails("terrain.waters");
302  view->unsetBrush();
303  } else
304  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_foliage_add") == true) {
305  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_NONE;
306  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_ADD;
307  view->unsetBrush();
308  setFoliageBrush();
310  } else
311  if (StringTools::startsWith(node->getId(), view->getTabId() + "_tab_foliage_delete") == true) {
312  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_NONE;
313  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_DELETE;
314  view->unsetBrush();
315  setFoliageBrush();
317  } else {
318  for (const auto& textureBrushApplyNode: textureBrushApplyNodes) {
319  if (node->getId() == textureBrushApplyNode) {
321  break;
322  }
323  }
324  for (const auto& foliageBrushApplyNode: foliageBrushApplyNodes) {
325  if (node->getId() == foliageBrushApplyNode) {
328  break;
329  }
330  }
331  for (const auto& foliageBrushPrototypeApplyNode: foliageBrushPrototypeApplyNodes) {
332  if (node->getId() == foliageBrushPrototypeApplyNode) {
335  break;
336  }
337  }
338  }
339 }
340 
342  if (basePropertiesSubController->onFocus(node, view->getPrototype()) == true) return;
343 }
344 
346  if (basePropertiesSubController->onUnfocus(node, view->getPrototype()) == true) return;
347 }
348 
350  if (node->getId() == "selectbox_outliner") {
351  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
352  if (StringTools::startsWith(outlinerNode, "terrain.waters.") == true) {
353  // clear
355  // delete
356  class OnTerrainWaterDelete: public virtual Action
357  {
358  public:
359  void performAction() override {
360  auto outlinerNode = terrainEditorTabController->view->getEditorView()->getScreenController()->getOutlinerSelection();
361  auto waterIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.waters.").size(), outlinerNode.size()));
362  terrainEditorTabController->deleteWater(waterIdx);
363  //
364  terrainEditorTabController->view->getEditorView()->reloadTabOutliner("terrain.waters");
365  }
366  OnTerrainWaterDelete(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
367  }
368  private:
369  TerrainEditorTabController* terrainEditorTabController;
370  };
371  popUps->getContextMenuScreenController()->addMenuItem("Delete Water", "contextmenu_delete", new OnTerrainWaterDelete(this));
372 
373  //
374  popUps->getContextMenuScreenController()->show(mouseX, mouseY);
375  } else
376  if (outlinerNode == "terrain.foliage") {
377  // clear
379  // add
380  class OnTerrainFoliageAddBrush: public virtual Action
381  {
382  public:
383  void performAction() override {
384  auto prototype = terrainEditorTabController->view->getPrototype();
385  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
386  if (terrain == nullptr) return;
387  auto brush = terrain->addBrush();
388  //
389  terrainEditorTabController->view->getEditorView()->reloadTabOutliner("terrain.foliage." + to_string(terrain->getBrushCount() - 1));
390  }
391  OnTerrainFoliageAddBrush(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
392  }
393  private:
394  TerrainEditorTabController* terrainEditorTabController;
395  };
396  popUps->getContextMenuScreenController()->addMenuItem("Add Foliage Brush", "contextmenu_add", new OnTerrainFoliageAddBrush(this));
397 
398  //
399  popUps->getContextMenuScreenController()->show(mouseX, mouseY);
400  } else
401  if (StringTools::startsWith(outlinerNode, "terrain.foliagebrushes.") == true) {
402  // clear
404 
405  // delete
406  class OnTerrainDeleteFoliageBrush: public virtual Action
407  {
408  public:
409  void performAction() override {
410  auto outlinerNode = terrainEditorTabController->view->getEditorView()->getScreenController()->getOutlinerSelection();
411  auto foliageBrushIdx = -1;
412  auto foliageBrushPrototypeIdx = -1;
413  if (terrainEditorTabController->checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
414  auto prototype = terrainEditorTabController->view->getPrototype();
415  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
416  if (terrain == nullptr) return;
417  auto brush = terrain->getBrush(foliageBrushIdx);
418  brush->removePrototype(foliageBrushPrototypeIdx);
419  auto newOutlinerNode = "terrain.brushes." + to_string(foliageBrushIdx);
420  //
421  terrainEditorTabController->view->getEditorView()->reloadTabOutliner(newOutlinerNode);
422  }
423  OnTerrainDeleteFoliageBrush(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
424  }
425  private:
426  TerrainEditorTabController* terrainEditorTabController;
427  };
428  popUps->getContextMenuScreenController()->addMenuItem("Delete Foliage Brush Prototype", "contextmenu_delete", new OnTerrainDeleteFoliageBrush(this));
429 
430  //
431  popUps->getContextMenuScreenController()->show(mouseX, mouseY);
432  } else
433  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
434  // clear
436 
437 
438  // add prototype
439  class OnTerrainAddFoliageBrushPrototype: public virtual Action
440  {
441  public:
442  void performAction() override {
443  auto outlinerNode = terrainEditorTabController->view->getEditorView()->getScreenController()->getOutlinerSelection();
444  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
445  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
446  auto prototype = terrainEditorTabController->view->getPrototype();
447  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
448  if (terrain == nullptr) return;
449  auto brush = terrain->getBrush(foliageBrushIdx);
450  if (brush == nullptr) return;
451  auto brushPrototype = brush->addPrototype();
452  auto newOutlinerNode = "terrain.foliagebrushes." + to_string(foliageBrushIdx) + "." + to_string(brush->getPrototypeCount() - 1);
453  //
454  terrainEditorTabController->view->getEditorView()->reloadTabOutliner(newOutlinerNode);
455  }
456  OnTerrainAddFoliageBrushPrototype(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
457  }
458  private:
459  TerrainEditorTabController* terrainEditorTabController;
460  };
461  popUps->getContextMenuScreenController()->addMenuItem("Add Foliage Prototype", "contextmenu_add", new OnTerrainAddFoliageBrushPrototype(this));
462 
463 
464  // delete
465  class OnTerrainDeleteFoliageBrush: public virtual Action
466  {
467  public:
468  void performAction() override {
469  auto outlinerNode = terrainEditorTabController->view->getEditorView()->getScreenController()->getOutlinerSelection();
470  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
471  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
472  auto prototype = terrainEditorTabController->view->getPrototype();
473  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
474  if (terrain == nullptr) return;
475  terrain->removeBrush(foliageBrushIdx);
476  //
477  terrainEditorTabController->view->getEditorView()->reloadTabOutliner("terrain.foliage");
478  }
479  OnTerrainDeleteFoliageBrush(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
480  }
481  private:
482  TerrainEditorTabController* terrainEditorTabController;
483  };
484  popUps->getContextMenuScreenController()->addMenuItem("Delete Foliage Brush", "contextmenu_delete", new OnTerrainDeleteFoliageBrush(this));
485 
486  //
487  popUps->getContextMenuScreenController()->show(mouseX, mouseY);
488  }
489  }
490  basePropertiesSubController->onContextMenuRequest(node, mouseX, mouseY, view->getPrototype());
491 }
492 
493 void TerrainEditorTabController::onTooltipShowRequest(GUINode* node, int mouseX, int mouseY) {
494  int tooltipLeft, tooltipTop;
495  if (view->getEditorView()->getCurrentTabTooltipPosition(screenNode, mouseX, mouseY, tooltipLeft, tooltipTop) == false) return;
496  //
497  popUps->getTooltipScreenController()->show(tooltipLeft, tooltipTop, node->getToolTip());
498 }
499 
502 }
503 
505 {
506  //
507  if (basePropertiesSubController->onAction(type, node, view->getPrototype()) == true) return;
508  //
509  if (type == GUIActionListenerType::PERFORMED) {
510  if (node->getId() == "terrain_create") {
511  onCreateTerrain();
512  } else
513  if (node->getId() == "foliagebrush_texture_open") {
514  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
515  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
516  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
517  auto prototype = view->getPrototype();
518  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
519  if (terrain == nullptr) return;
520  auto brush = terrain->getBrush(foliageBrushIdx);
521  if (brush == nullptr) return;
522 
523  //
524  class OnFoliageBrushFileOpenAction: public virtual Action
525  {
526  public:
527  void performAction() override {
528  terrainEditorTabController->setFoliageBrushTexture(
529  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getPathName() +
530  "/" +
531  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getFileName()
532  );
533  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->close();
534  }
535 
536  /**
537  * Public constructor
538  * @param terrainEditorTabController terrain editor tab controller
539  */
540  OnFoliageBrushFileOpenAction(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
541  }
542 
543  private:
544  TerrainEditorTabController* terrainEditorTabController;
545  };
546 
547  vector<string> extensions = TextureReader::getTextureExtensions();
549  brush->getFileName().empty() == false?Tools::getPathName(brush->getFileName()):string(),
550  "Load foliage brush texture from: ",
551  extensions,
552  Tools::getFileName(brush->getFileName()),
553  true,
554  new OnFoliageBrushFileOpenAction(this)
555  );
556  } else
557  if (node->getId() == "foliagebrush_texture_remove") {
558  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
559  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
560  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
561  auto prototype = view->getPrototype();
562  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
563  if (terrain == nullptr) return;
564  auto brush = terrain->getBrush(foliageBrushIdx);
565  if (brush == nullptr) return;
566  brush->setFileName(string());
567  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_texture"))->setSource(brush->getFileName());
569  } else
570  if (node->getId() == "foliagebrush_texture_browseto") {
571  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
572  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
573  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
574  auto prototype = view->getPrototype();
575  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
576  if (terrain == nullptr) {
577  showInfoPopUp("Browse To", "Nothing to browse to");
578  return;
579  }
580  auto brush = terrain->getBrush(foliageBrushIdx);
581  if (brush == nullptr || brush->getFileName().empty() == true) {
582  showInfoPopUp("Browse To", "Nothing to browse to");
583  return;
584  }
585  view->getEditorView()->getScreenController()->browseTo(brush->getFileName());
586  } else
587  if (node->getId() == "foliagebrush_prototype_file_open") {
588  class OnTerrainBrushPrototypeFileOpenAction: public virtual Action
589  {
590  public:
591  void performAction() override {
592  terrainEditorTabController->setFoliageBrushPrototype(
593  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getPathName() +
594  "/" +
595  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getFileName()
596  );
597  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->close();
598  }
599 
600  /**
601  * Public constructor
602  * @param terrainEditorTabController terrain editor tab controller
603  */
604  OnTerrainBrushPrototypeFileOpenAction(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
605  }
606 
607  private:
608  TerrainEditorTabController* terrainEditorTabController;
609  };
610 
611  auto prototype = view->getPrototype();
612  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
613  if (terrain == nullptr) return;
614 
615  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
616  auto foliageBrushIdx = -1;
617  auto foliageBrushPrototypeIdx = -1;
618  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
619  auto brush = terrain->getBrush(foliageBrushIdx);
620  if (brush == nullptr) return;
621  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
622  if (brushPrototype == nullptr) return;
623 
625  brushPrototype->getFileName().empty() == false?Tools::getPathName(brushPrototype->getFileName()):string(),
626  "Load terrain brush texture from: ",
627  PrototypeReader::getPrototypeExtensions(),
628  Tools::getFileName(brushPrototype->getFileName()),
629  true,
630  new OnTerrainBrushPrototypeFileOpenAction(this)
631  );
632  } else
633  if (node->getId() == "foliagebrush_prototype_file_remove") {
634  auto prototype = view->getPrototype();
635  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
636  if (terrain == nullptr) return;
637 
638  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
639  auto foliageBrushIdx = -1;
640  auto foliageBrushPrototypeIdx = -1;
641  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
642  auto brush = terrain->getBrush(foliageBrushIdx);
643  if (brush == nullptr) return;
644  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
645  if (brushPrototype == nullptr) return;
646  brushPrototype->setFileName(string());
648  try {
649  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_prototype_file"))->setSource(brushPrototype->getFileName());
650  } catch (Exception& exception) {
651  Console::println("TerrainEditorTabController::onAction(): " + string(exception.what()));
652  }
653  } else
654  if (node->getId() == "foliagebrush_prototype_file_browseto") {
655  auto prototype = view->getPrototype();
656  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
657  if (terrain == nullptr) return;
658 
659  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
660  auto foliageBrushIdx = -1;
661  auto foliageBrushPrototypeIdx = -1;
662  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
663  auto brush = terrain->getBrush(foliageBrushIdx);
664  if (brush == nullptr) {
665  showInfoPopUp("Browse To", "Nothing to browse to");
666  return;
667  }
668  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
669  if (brushPrototype == nullptr || brushPrototype->getFileName().empty() == true) {
670  showInfoPopUp("Browse To", "Nothing to browse to");
671  return;
672  }
673  view->getEditorView()->getScreenController()->browseTo(brushPrototype->getFileName());
674  } else
675  if (node->getId() == "terrainbrush_texture_open") {
676  class OnTerrainBrushFileOpenAction: public virtual Action
677  {
678  public:
679  void performAction() override {
680  terrainEditorTabController->setTerrainBrushTexture(
681  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getPathName() +
682  "/" +
683  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->getFileName()
684  );
685  terrainEditorTabController->view->getPopUps()->getFileDialogScreenController()->close();
686  }
687 
688  /**
689  * Public constructor
690  * @param terrainEditorTabController terrain editor tab controller
691  */
692  OnTerrainBrushFileOpenAction(TerrainEditorTabController* terrainEditorTabController): terrainEditorTabController(terrainEditorTabController) {
693  }
694 
695  private:
696  TerrainEditorTabController* terrainEditorTabController;
697  };
698 
699  vector<string> extensions = TextureReader::getTextureExtensions();
701  currentTerrainBrushTextureFileName.empty() == false?Tools::getPathName(currentTerrainBrushTextureFileName):string(),
702  "Load terrain brush texture from: ",
703  extensions,
704  Tools::getFileName(currentTerrainBrushTextureFileName),
705  true,
706  new OnTerrainBrushFileOpenAction(this)
707  );
708  } else
709  if (node->getId() == "terrainbrush_texture_remove") {
711  currentTerrainBrushTexture = nullptr;
713  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTexture(currentTerrainBrushTexture);
714  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTooltip(currentTerrainBrushTextureFileName);
715  } else
716  if (node->getId() == "terrainbrush_texture_browseto") {
717  if (currentTerrainBrushTextureFileName.empty() == true) {
718  showInfoPopUp("Browse To", "Nothing to browse to");
719  } else {
721  }
722  } else
723  if (node->getId() == "terrain_mirrormode_apply") {
724  auto prototype = view->getPrototype();
725  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
726  if (terrain == nullptr) return;
727  auto mirrorMode = required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_mirrormode"))->getController()->getValue().getString();
728  auto flipped = required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_mirrormode_flipped"))->getController()->getValue().getString() == "1";
729  view->reset();
730  if (mirrorMode == "1") {
731  // no op
732  } else
733  if (mirrorMode == "2") {
734  Terrain::mirrorXAxis(
735  flipped,
736  terrain->getWidth(),
737  terrain->getDepth(),
738  terrain->getHeightVector(),
739  terrain->getWaterPositionMapsHeight(),
740  terrain->getWaterPositionMaps(),
741  terrain->getFoliageMaps()
742  );
743  terrain->setWidth(terrain->getWidth() * 2.0f);
744  } else
745  if (mirrorMode == "3") {
746  Terrain::mirrorZAxis(
747  flipped,
748  terrain->getWidth(),
749  terrain->getDepth(),
750  terrain->getHeightVector(),
751  terrain->getWaterPositionMapsHeight(),
752  terrain->getWaterPositionMaps(),
753  terrain->getFoliageMaps()
754  );
755  terrain->setDepth(terrain->getDepth() * 2.0f);
756  } else
757  if (mirrorMode == "4") {
758  Terrain::mirrorXAxis(
759  flipped,
760  terrain->getWidth(),
761  terrain->getDepth(),
762  terrain->getHeightVector(),
763  terrain->getWaterPositionMapsHeight(),
764  terrain->getWaterPositionMaps(),
765  terrain->getFoliageMaps()
766  );
767  terrain->setWidth(terrain->getWidth() * 2.0f);
768  Terrain::mirrorZAxis(
769  flipped,
770  terrain->getWidth(),
771  terrain->getDepth(),
772  terrain->getHeightVector(),
773  terrain->getWaterPositionMapsHeight(),
774  terrain->getWaterPositionMaps(),
775  terrain->getFoliageMaps()
776  );
777  terrain->setDepth(terrain->getDepth() * 2.0f);
778  }
780  }
781  }
782 }
783 
785  auto prototype = view->getPrototype();
786  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
787  string xml;
788  xml+= "<selectbox-parent-option image=\"resources/engine/images/folder.png\" text=\"" + GUIParser::escape("Terrain") + "\" value=\"" + GUIParser::escape("terrain") + "\">\n";
789  basePropertiesSubController->createBasePropertiesXML(view->getPrototype(), xml);
790  xml+= "<selectbox-option image=\"resources/engine/images/terrain.png\" text=\"" + GUIParser::escape("Terrain Brush") + "\" value=\"" + GUIParser::escape("terrain.brush") + "\" />\n";
791  if (terrain != nullptr && terrain->getWaterPositionMapsIndices().empty() == false) {
792  xml+= "<selectbox-parent-option image=\"resources/engine/images/terrain_water.png\" text=\"" + GUIParser::escape("Water") + "\" value=\"" + GUIParser::escape("terrain.waters") + "\">\n";
793  auto i = 0;
794  for (auto waterIdx: terrain->getWaterPositionMapsIndices()) {
795  xml+= "<selectbox-option image=\"resources/engine/images/terrain_water.png\" text=\"" + GUIParser::escape("Water " + to_string(i)) + "\" value=\"" + GUIParser::escape("terrain.waters." + to_string(waterIdx)) + "\" />\n";
796  i++;
797  }
798  xml+= "</selectbox-parent-option>\n";
799  } else {
800  xml+= "<selectbox-option image=\"resources/engine/images/terrain_water.png\" text=\"" + GUIParser::escape("Water") + "\" value=\"" + GUIParser::escape("terrain.waters") + "\" />\n";
801  }
802  if (terrain != nullptr && terrain->getBrushCount() > 0) {
803  xml+= "<selectbox-parent-option image=\"resources/engine/images/folder.png\" text=\"" + GUIParser::escape("Foliage") + "\" value=\"" + GUIParser::escape("terrain.foliage") + "\">\n";
804  auto i = 0;
805  for (auto brush: terrain->getBrushes()) {
806  if (brush->getPrototypeCount() > 0) {
807  xml+= "<selectbox-parent-option image=\"resources/engine/images/foliage.png\" text=\"" + GUIParser::escape("Foliage Brush " + to_string(i)) + "\" value=\"" + GUIParser::escape("terrain.foliage." + to_string(i)) + "\" >\n";
808  auto j = 0;
809  for (auto brushPrototype: brush->getPrototypes()) {
810  xml+= "<selectbox-option image=\"resources/engine/images/mesh.png\" text=\"" + GUIParser::escape("Prototype " + to_string(j)) + "\" value=\"" + GUIParser::escape("terrain.foliagebrushes." + to_string(i) + "." + to_string(j)) + "\" />\n";
811  j++;
812  }
813  xml+= "</selectbox-parent-option>\n";
814  } else {
815  xml+= "<selectbox-option image=\"resources/engine/images/foliage.png\" text=\"" + GUIParser::escape("Foliage Brush " + to_string(i)) + "\" value=\"" + GUIParser::escape("terrain.foliage." + to_string(i)) + "\" />\n";
816  }
817  i++;
818  }
819  xml+= "</selectbox-parent-option>\n";
820  } else {
821  xml+= "<selectbox-option image=\"resources/engine/images/folder.png\" text=\"" + GUIParser::escape("Foliage") + "\" value=\"" + GUIParser::escape("terrain.foliage") + "\" />\n";
822  }
823  auto foliagePrototypeIndices = terrain != nullptr?terrain->getFoliagePrototypeIndices():vector<int>();
824  if (foliagePrototypeIndices.empty() == false) {
825  xml+= "<selectbox-parent-option image=\"resources/engine/images/folder.png\" text=\"" + GUIParser::escape("Prototypes") + "\" value=\"" + GUIParser::escape("terrain.foliageprototypes") + "\">\n";
826  auto i = 0;
827  for (auto prototypeIdx: foliagePrototypeIndices) {
828  auto foliagePrototype = terrain->getFoliagePrototype(prototypeIdx);
829  xml+= "<selectbox-option image=\"resources/engine/images/mesh.png\" text=\"" + GUIParser::escape(Tools::removeFileExtension(Tools::getFileName(foliagePrototype->getFileName()))) + "\" value=\"" + GUIParser::escape("terrain.foliageprototypes." + to_string(prototypeIdx)) + "\" />\n";
830  i++;
831  }
832  xml+= "</selectbox-parent-option>\n";
833  }
834  xml+= "</selectbox-parent-option>\n";
836 }
837 
840 }
841 
843  auto prototype = view->getPrototype();
844  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
845  if (terrain == nullptr) return;
846 
847  //
848  try {
849  view->reset();
850  auto width = Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_size_x"))->getController()->getValue().getString());
851  auto depth = Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_size_z"))->getController()->getValue().getString());
852  if (width < 1.0f || width > 4000.0f) throw ExceptionBase("Width must be within 1 .. 4000");
853  if (depth < 1.0f || depth > 4000.0f) throw ExceptionBase("Depth must be within 1 .. 4000");
854  terrain->getHeightVector().clear();
855  for (auto idx: terrain->getWaterPositionMapsIndices()) terrain->removeWaterPositionMap(idx);
856  BoundingBox terrainBoundingBox;
857  vector<Model*> terrainModels;
858  Terrain::createTerrainModels(width, depth, 0.0f, terrain->getHeightVector(), terrainBoundingBox, terrainModels);
859  Terrain::createFoliageMaps(terrainBoundingBox, terrain->getFoliageMaps());
860  Terrain::createFoliageMaps(terrainBoundingBox, newFoliageMaps);
861  terrain->setWidth(terrainBoundingBox.getDimensions().getX());
862  terrain->setDepth(terrainBoundingBox.getDimensions().getZ());
864  } catch (Exception& exception) {
865  showInfoPopUp("Warning", string(exception.what()));
866  }
867 
868  //
869  view->getEditorView()->reloadTabOutliner("terrain");
870 }
871 
873  if (currentTerrainBrushOperation != Terrain::BRUSHOPERATION_NONE) {
874  currentTerrainBrushScale = scale;
875  if (view->getEditorView()->getScreenController()->getOutlinerSelection() == "terrain.brush") {
877  }
878  } else
879  if (currentFoliageBrushOperation != Terrain::BRUSHOPERATION_NONE) {
880  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
881  auto foliageBrushIdx = -1;
882  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
883  foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
884  } else {
885  auto foliageBrushPrototypeIdx = -1;
886  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
887  }
888  auto prototype = view->getPrototype();
889  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
890  if (terrain == nullptr) return;
891  auto brush = terrain->getBrush(foliageBrushIdx);
892  if (brush == nullptr) return;
893  brush->setSize(scale);
894  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) updateFoliageBrushDetails();
896  }
897 }
898 
900  if (currentTerrainBrushOperation != Terrain::BRUSHOPERATION_NONE) {
901  currentTerrainBrushStrength = densityStrength;
902  if (view->getEditorView()->getScreenController()->getOutlinerSelection() == "terrain.brush") {
904  }
905  } else
906  if (currentFoliageBrushOperation != Terrain::BRUSHOPERATION_NONE) {
907  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
908  auto foliageBrushIdx = -1;
909  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
910  foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
911  } else {
912  auto foliageBrushPrototypeIdx = -1;
913  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
914  }
915  auto prototype = view->getPrototype();
916  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
917  if (terrain == nullptr) return;
918  auto brush = terrain->getBrush(foliageBrushIdx);
919  if (brush == nullptr) return;
920  brush->setDensity(densityStrength);
921  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) updateFoliageBrushDetails();
923  }
924 }
925 
927  auto prototype = view->getPrototype();
928  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
929  if (terrain == nullptr) return;
930 
931  //
933  "<template id=\"details_terrain\" src=\"resources/engine/gui/template_details_terrain.xml\" />\n"
934  );
935 
936  //
937  try {
938  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("details_terrain"))->getActiveConditions().add("open");
939  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_size_x"))->getController()->setValue(MutableString(terrain->getWidth()));
940  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrain_size_z"))->getController()->setValue(MutableString(terrain->getDepth()));
941  } catch (Exception& exception) {
942  Console::println("TerrainEditorTabController::setTerrainDetails(): An error occurred: " + string(exception.what()));
943  showInfoPopUp("Warning", string(exception.what()));
944  }
945 }
946 
949  "<template id=\"details_terrainbrush\" src=\"resources/engine/gui/template_details_terrainbrush.xml\" />\n"
950  );
951 
952  //
953  try {
954  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("details_terrainbrush"))->getActiveConditions().add("open");
955  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTexture(currentTerrainBrushTexture);
956  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTooltip(currentTerrainBrushTextureFileName);
957  } catch (Exception& exception) {
958  Console::println("TerrainEditorTabController::setTerrainBrushDetails(): An error occurred: " + string(exception.what()));
959  showInfoPopUp("Warning", string(exception.what()));
960  }
961 
962  //
964 }
965 
967  try {
968  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrainbrush_size"))->getController()->setValue(MutableString(currentTerrainBrushScale));
969  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrainbrush_strength"))->getController()->setValue(MutableString(currentTerrainBrushStrength));
970  } catch (Exception& exception) {
971  Console::println("TerrainEditorTabController::updateTerrainBrushDetails(): An error occurred: " + string(exception.what()));
972  showInfoPopUp("Warning", string(exception.what()));
973  }
974 }
975 
977  try {
978  currentTerrainBrushScale = Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrainbrush_size"))->getController()->getValue().getString()); // TODO: a.drewke, size != scale
979  currentTerrainBrushStrength = Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("terrainbrush_strength"))->getController()->getValue().getString());
980  } catch (Exception& exception) {
981  Console::println("TerrainEditorTabController::setTerrainBrushDetails(): An error occurred: " + string(exception.what()));
982  showInfoPopUp("Warning", string(exception.what()));
983  }
984  // TODO: a.drewke, maybe improve me
986 }
987 
990  "<template id=\"details_foliagebrush\" src=\"resources/engine/gui/template_details_foliagebrush.xml\" />\n"
991  );
992 
993  //
994  try {
995  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("details_foliagebrush"))->getActiveConditions().add("open");
996  } catch (Exception& exception) {
997  Console::println("TerrainEditorTabController::setFoliageBrushDetails(): An error occurred: " + string(exception.what()));
998  showInfoPopUp("Warning", string(exception.what()));
999  }
1000 
1001  //
1003 }
1004 
1006  //
1007  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1008  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
1009  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
1010  auto prototype = view->getPrototype();
1011  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1012  if (terrain == nullptr) return;
1013  auto brush = terrain->getBrush(foliageBrushIdx);
1014  if (brush == nullptr) return;
1015 
1016  //
1017  try {
1018  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_texture"))->setSource(brush->getFileName());
1019  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_texture"))->setTooltip(brush->getFileName());
1020  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_size"))->getController()->setValue(MutableString(brush->getSize()));
1021  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_density"))->getController()->setValue(MutableString(brush->getDensity()));
1022  } catch (Exception& exception) {
1023  Console::println("TerrainEditorTabController::updateFoliageBrushDetails(): An error occurred: " + string(exception.what()));
1024  showInfoPopUp("Warning", string(exception.what()));
1025  }
1026 }
1027 
1029  //
1030  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1031  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
1032  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
1033  auto prototype = view->getPrototype();
1034  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1035  if (terrain == nullptr) return;
1036  auto brush = terrain->getBrush(foliageBrushIdx);
1037  if (brush == nullptr) return;
1038 
1039  //
1040  try {
1041  brush->setSize(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_size"))->getController()->getValue().getString()));
1042  brush->setDensity(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_density"))->getController()->getValue().getString()));
1043  } catch (Exception& exception) {
1044  Console::println("TerrainEditorTabController::applyFoliageBrushDetails(): An error occurred: " + string(exception.what()));
1045  showInfoPopUp("Warning", string(exception.what()));
1046  }
1047 }
1048 
1051  "<template id=\"details_foliagebrush_prototype\" src=\"resources/engine/gui/template_details_foliagebrush_prototype.xml\" />\n"
1052  );
1053 
1054  auto prototype = view->getPrototype();
1055  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1056  if (terrain == nullptr) return;
1057 
1058  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1059  auto foliageBrushIdx = -1;
1060  auto foliageBrushPrototypeIdx = -1;
1061  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1062  auto brush = terrain->getBrush(foliageBrushIdx);
1063  if (brush == nullptr) return;
1064  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
1065  if (brushPrototype == nullptr) return;
1066 
1067  //
1068  try {
1069  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("details_foliagebrush_prototype"))->getActiveConditions().add("open");
1070 
1071  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_prototype_file"))->setSource(brushPrototype->getFileName());
1072  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_prototype_file"))->setTooltip(brushPrototype->getFileName());
1073  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_object_count"))->getController()->setValue(MutableString(brushPrototype->getCount()));
1074  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_normalalign"))->getController()->setValue(MutableString(brushPrototype->isNormalAlign() == true?"1":""));
1075  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_x_min"))->getController()->setValue(MutableString(brushPrototype->getRotationXMin()));
1076  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_x_max"))->getController()->setValue(MutableString(brushPrototype->getRotationXMax()));
1077  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_y_min"))->getController()->setValue(MutableString(brushPrototype->getRotationYMin()));
1078  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_y_max"))->getController()->setValue(MutableString(brushPrototype->getRotationYMax()));
1079  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_z_min"))->getController()->setValue(MutableString(brushPrototype->getRotationZMin()));
1080  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_z_max"))->getController()->setValue(MutableString(brushPrototype->getRotationZMax()));
1081  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_scalerange_min"))->getController()->setValue(MutableString(brushPrototype->getScaleMin()));
1082  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_scalerange_max"))->getController()->setValue(MutableString(brushPrototype->getScaleMax()));
1083  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_heightrange_min"))->getController()->setValue(MutableString(brushPrototype->getHeightMin()));
1084  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_heightrange_max"))->getController()->setValue(MutableString(brushPrototype->getHeightMax()));
1085  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_sloperange_min"))->getController()->setValue(MutableString(brushPrototype->getSlopeMin()));
1086  required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_sloperange_max"))->getController()->setValue(MutableString(brushPrototype->getSlopeMax()));
1087  } catch (Exception& exception) {
1088  Console::println("TerrainEditorTabController::setFoliageBrushPrototypeDetails(): An error occurred: " + string(exception.what()));
1089  showInfoPopUp("Warning", string(exception.what()));
1090  }
1091 }
1092 
1094  auto prototype = view->getPrototype();
1095  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1096  if (terrain == nullptr) return;
1097 
1098  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1099  auto foliageBrushIdx = -1;
1100  auto foliageBrushPrototypeIdx = -1;
1101  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1102  auto brush = terrain->getBrush(foliageBrushIdx);
1103  if (brush == nullptr) return;
1104  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
1105  if (brushPrototype == nullptr) return;
1106 
1107  try {
1108  brushPrototype->setCount(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_object_count"))->getController()->getValue().getString()));
1109  brushPrototype->setNormalAlign(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_normalalign"))->getController()->getValue().getString() == "1");
1110  brushPrototype->setRotationXMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_x_min"))->getController()->getValue().getString()));
1111  brushPrototype->setRotationXMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_x_max"))->getController()->getValue().getString()));
1112  brushPrototype->setRotationYMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_y_min"))->getController()->getValue().getString()));
1113  brushPrototype->setRotationYMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_y_max"))->getController()->getValue().getString()));
1114  brushPrototype->setRotationZMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_z_min"))->getController()->getValue().getString()));
1115  brushPrototype->setRotationZMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_rotationrange_z_max"))->getController()->getValue().getString()));
1116  brushPrototype->setScaleMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_scalerange_min"))->getController()->getValue().getString()));
1117  brushPrototype->setScaleMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_scalerange_max"))->getController()->getValue().getString()));
1118  brushPrototype->setHeightMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_heightrange_min"))->getController()->getValue().getString()));
1119  brushPrototype->setHeightMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_heightrange_max"))->getController()->getValue().getString()));
1120  brushPrototype->setSlopeMin(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_sloperange_min"))->getController()->getValue().getString()));
1121  brushPrototype->setSlopeMax(Float::parse(required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById("foliagebrush_prototype_sloperange_max"))->getController()->getValue().getString()));
1122  } catch (Exception& exception) {
1123  Console::println("TerrainEditorTabController::applyFoliageBrushPrototypeDetails(): An error occurred: " + string(exception.what()));
1124  showInfoPopUp("Warning", string(exception.what()));
1125  }
1126 }
1127 
1128 bool TerrainEditorTabController::checkOutlinerFoliageBrushPrototype(const string& outlinerNode, int& foliageBrushIdx, int& foliageBrushPrototypeIdx) {
1129  if (StringTools::startsWith(outlinerNode, "terrain.foliagebrushes.") == false) return false;
1130  auto brushIdxBeginIdx = string("terrain.foliagebrushes.").size();
1131  auto brushIdxEndIdx = outlinerNode.find('.', brushIdxBeginIdx + 1);
1132  auto prototypeBeginIdx = brushIdxEndIdx + 1;
1133  auto prototypeEndIdx = outlinerNode.size();
1134  foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, brushIdxBeginIdx, brushIdxEndIdx));
1135  foliageBrushPrototypeIdx = Integer::parse(StringTools::substring(outlinerNode, prototypeBeginIdx, prototypeEndIdx));
1136  return true;
1137 }
1138 
1139 void TerrainEditorTabController::updateDetails(const string& outlinerNode) {
1140  view->getEditorView()->setDetailsContent(string());
1141  if (outlinerNode == "terrain") {
1144  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
1146  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().set("terrain");
1147  } else
1148  if (outlinerNode == "terrain.brush") {
1151  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
1152  view->unsetBrush();
1153  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().set("terrain");
1154  } else
1155  if (outlinerNode == "terrain.waters" || StringTools::startsWith(outlinerNode, "terrain.waters.") == true) {
1157  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
1158  view->unsetBrush();
1159  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().set("water");
1160  } else
1161  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
1163  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_NONE;
1166  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().set("foliage");
1167  } else
1168  if (StringTools::startsWith(outlinerNode, "terrain.foliagebrushes.") == true) {
1170  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_NONE;
1173  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().set("foliage");
1174  } else {
1175  view->unsetBrush();
1176  currentTerrainBrushOperation = Terrain::BRUSHOPERATION_NONE;
1177  currentFoliageBrushOperation = Terrain::BRUSHOPERATION_NONE;
1178  required_dynamic_cast<GUIElementNode*>(view->getEditorView()->getScreenController()->getScreenNode()->getNodeById(view->getTabId() + "_tab_toolbar"))->getActiveConditions().removeAll();
1179  basePropertiesSubController->updateDetails(view->getPrototype(), outlinerNode);
1180  }
1181 }
1182 
1184  auto prototype = view->getPrototype();
1185  if (prototype == nullptr) return;
1186 
1187  //
1188  try {
1189  auto width = prototype->getTerrain()->getWidth();
1190  auto depth = prototype->getTerrain()->getDepth();
1191  BoundingBox terrainBoundingBox;
1192  vector<Model*> terrainModels;
1193  Terrain::createTerrainModels(width, depth, 0.0f, prototype->getTerrain()->getHeightVector(), terrainBoundingBox, terrainModels);
1194  view->unsetWater();
1195  view->setTerrain(terrainBoundingBox, terrainModels);
1196  auto waterPositionMapsIndices = prototype->getTerrain()->getWaterPositionMapsIndices();
1197  for (auto waterPositionMapIdx: waterPositionMapsIndices) {
1198  vector<Model*> waterModels;
1199  Terrain::createWaterModels(
1200  terrainBoundingBox,
1201  prototype->getTerrain()->getWaterPositionMap(waterPositionMapIdx),
1202  prototype->getTerrain()->getWaterPositionMapHeight(waterPositionMapIdx),
1203  waterPositionMapIdx,
1204  waterModels
1205  );
1206  view->addWater(
1207  waterPositionMapIdx,
1208  waterModels,
1209  Terrain::computeWaterReflectionEnvironmentMappingPosition(
1210  prototype->getTerrain()->getWaterPositionMap(waterPositionMapIdx),
1211  prototype->getTerrain()->getWaterPositionMapHeight(waterPositionMapIdx)
1212  )
1213  );
1214  }
1215  view->addFoliage();
1216  Terrain::createFoliageMaps(terrainBoundingBox, newFoliageMaps);
1217  prototype->getTerrain()->setWidth(terrainBoundingBox.getDimensions().getX());
1218  prototype->getTerrain()->setDepth(terrainBoundingBox.getDimensions().getZ());
1219  } catch (Exception& exception) {
1220  showInfoPopUp("Warning", string(exception.what()));
1221  }
1222 }
1223 
1224 void TerrainEditorTabController::applyTerrainBrush(BoundingBox& terrainBoundingBox, vector<Model*>& terrainModels, const Vector3& brushCenterPosition, int64_t deltaTime) {
1225  auto prototype = view->getPrototype();
1226  if (prototype == nullptr) return;
1227  if (terrainModels.empty() == true) return;
1228 
1229  // apply brush first
1230  Terrain::applyBrushToTerrainModels(
1231  terrainBoundingBox,
1232  terrainModels,
1233  prototype->getTerrain()->getHeightVector(),
1234  brushCenterPosition,
1237  currentTerrainBrushStrength * static_cast<float>(deltaTime) / 200.0f, // if strength = 1.0f it will e.g. add to level 5 meters/second
1240  );
1241 
1242  //
1243  Terrain::FoliageBrush foliageBrush = {
1244  foliageBrush.brushTexture = Terrain::BRUSHOPERATION_RAMP?rampTerrainBrushTexture:currentTerrainBrushTexture,
1246  foliageBrush.brushDensity = 1.0f
1247  };
1248 
1249  // and update foliage
1250  Terrain::updateFoliageTerrainBrush(
1251  terrainBoundingBox,
1252  prototype->getTerrain()->getHeightVector(),
1253  brushCenterPosition,
1254  foliageBrush,
1255  prototype->getTerrain()->getFoliageMaps(),
1257  );
1258 
1259  //
1261  recreateFoliagePartitions.clear();
1262 }
1263 
1264 bool TerrainEditorTabController::determineCurrentBrushHeight(BoundingBox& terrainBoundingBox, vector<Model*> terrainModels, const Vector3& brushCenterPosition) {
1265  auto prototype = view->getPrototype();
1266  if (prototype == nullptr) return false;
1267  if (currentTerrainBrushOperation != Terrain::BRUSHOPERATION_FLATTEN &&
1268  currentTerrainBrushOperation != Terrain::BRUSHOPERATION_WATER_ADD &&
1269  currentTerrainBrushOperation != Terrain::BRUSHOPERATION_RAMP) {
1270  return true;
1271  }
1272  if (haveCurrentTerrainBrushHeight == true) return true;
1273  if (terrainModels.empty() == true) return false;
1274  auto terrainModel = terrainModels[0];
1275  if (terrainModel == nullptr) return false;
1276  haveCurrentTerrainBrushHeight = Terrain::getTerrainModelsHeight(
1277  terrainBoundingBox,
1278  terrainModels,
1279  prototype->getTerrain()->getHeightVector(),
1280  brushCenterPosition,
1282  );
1284 }
1285 
1286 bool TerrainEditorTabController::determineRampHeight(BoundingBox& terrainBoundingBox, vector<Model*> terrainModels, const Vector3& position, float& height) {
1287  auto prototype = view->getPrototype();
1288  if (prototype == nullptr) return false;
1289  if (currentTerrainBrushOperation != Terrain::BRUSHOPERATION_RAMP) {
1290  return false;
1291  }
1292  if (terrainModels.empty() == true) return false;
1293  auto terrainModel = terrainModels[0];
1294  if (terrainModel == nullptr) return false;
1295  return Terrain::getTerrainModelsHeight(
1296  terrainBoundingBox,
1297  terrainModels,
1298  prototype->getTerrain()->getHeightVector(),
1299  position,
1300  height
1301  );
1302 }
1303 
1306 }
1307 
1308 void TerrainEditorTabController::applyRampTerrainBrush(BoundingBox& terrainBoundingBox, vector<Model*>& terrainModels, const Vector3& position, float rotation, const Vector2& scale, float minHeight, float maxHeight) {
1309  auto prototype = view->getPrototype();
1310  if (prototype == nullptr) return;
1311  if (terrainModels.empty() == true) return;
1312 
1313  // apply brush first
1314  Terrain::applyRampBrushToTerrainModels(
1315  terrainBoundingBox,
1316  terrainModels,
1317  prototype->getTerrain()->getHeightVector(),
1318  position,
1320  rotation,
1321  scale,
1322  minHeight,
1323  maxHeight
1324  );
1325 
1326  // and update foliage
1327  Terrain::updateFoliageTerrainRampBrush(
1328  terrainBoundingBox,
1329  prototype->getTerrain()->getHeightVector(),
1330  position,
1332  rotation,
1333  scale,
1334  prototype->getTerrain()->getFoliageMaps(),
1336  );
1337 
1338  //
1340  recreateFoliagePartitions.clear();
1341 }
1342 
1343 void TerrainEditorTabController::createWater(BoundingBox& terrainBoundingBox, const Vector3& brushCenterPosition, vector<Model*>& waterModels, Vector3& waterReflectionEnvironmentMappingPosition) {
1344  auto prototype = view->getPrototype();
1345  if (prototype == nullptr) return;
1346  auto waterPositionMapIdx = prototype->getTerrain()->allocateWaterPositionMapIdx();
1347  prototype->getTerrain()->setWaterPositionMapHeight(waterPositionMapIdx, currentTerrainBrushHeight);
1348  if (Terrain::computeWaterPositionMap(
1349  terrainBoundingBox,
1350  prototype->getTerrain()->getHeightVector(),
1351  brushCenterPosition,
1352  prototype->getTerrain()->getWaterPositionMapHeight(waterPositionMapIdx),
1353  prototype->getTerrain()->getWaterPositionMap(waterPositionMapIdx)) == true) {
1354  //
1355  Terrain::createWaterModels(
1356  terrainBoundingBox,
1357  prototype->getTerrain()->getWaterPositionMap(waterPositionMapIdx),
1358  prototype->getTerrain()->getWaterPositionMapHeight(waterPositionMapIdx),
1359  waterPositionMapIdx,
1360  waterModels
1361  );
1362  waterReflectionEnvironmentMappingPosition = Terrain::computeWaterReflectionEnvironmentMappingPosition(
1363  prototype->getTerrain()->getWaterPositionMap(waterPositionMapIdx),
1364  prototype->getTerrain()->getWaterPositionMapHeight(waterPositionMapIdx)
1365  );
1366  view->addWater(
1367  waterPositionMapIdx,
1368  waterModels,
1369  waterReflectionEnvironmentMappingPosition
1370  );
1371  }
1372  view->getEditorView()->reloadTabOutliner("terrain.waters." + to_string(waterPositionMapIdx));
1373 }
1374 
1375 void TerrainEditorTabController::deleteWater(int waterPositionMapIdx) {
1376  auto prototype = view->getPrototype();
1377  if (prototype == nullptr) return;
1378  prototype->getTerrain()->removeWaterPositionMap(waterPositionMapIdx);
1379  view->removeWater(waterPositionMapIdx);
1380  view->getEditorView()->reloadTabOutliner("terrain.waters");
1381 }
1382 
1384  auto prototype = view->getPrototype();
1385  if (prototype == nullptr) return;
1386 
1387  //
1388  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1389  auto foliageBrushIdx = -1;
1390  auto foliageBrushPrototypeIdx = -1;
1391  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1392  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1393  if (terrain == nullptr) return;
1394  auto brush = terrain->getBrush(foliageBrushIdx);
1395  if (brush == nullptr) return;
1396 
1397  // texture
1398  auto foliageBrushTexture = TextureReader::read(Tools::getPathName(brush->getFileName()), Tools::getFileName(brush->getFileName()), false, false);
1399  if (foliageBrushTexture == nullptr) return;
1400 
1401  //
1402  view->setBrush(foliageBrushTexture, brush->getSize(), brush->getDensity());
1403 }
1404 
1406  auto prototype = view->getPrototype();
1407  if (prototype == nullptr) return;
1408 
1409  //
1410  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1411  auto foliageBrushIdx = -1;
1412  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
1413  foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
1414  } else {
1415  auto foliageBrushPrototypeIdx = -1;
1416  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1417  }
1418 
1419  //
1420  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1421  if (terrain == nullptr) return;
1422  auto brush = terrain->getBrush(foliageBrushIdx);
1423  if (brush == nullptr) return;
1424 
1425  //
1426  if (foliageBrush.brushTexture != nullptr) foliageBrush.brushTexture->releaseReference();
1427 
1428  //
1429  foliageBrush.brushTexture = TextureReader::read(Tools::getPathName(brush->getFileName()), Tools::getFileName(brush->getFileName()), false, false);
1430  foliageBrush.brushScale = brush->getSize();
1431  foliageBrush.brushDensity = brush->getDensity();
1432  if (foliageBrush.brushTexture != nullptr) foliageBrush.brushTexture->acquireReference();
1433 
1434  //
1435  foliageBrushPrototypes.clear();
1436  for (auto foliageBrushPrototype: brush->getPrototypes()) {
1437  Prototype* foliagePrototype = nullptr;
1438  if (foliageBrushPrototype->getFileName().empty() == false) {
1439  try {
1440  foliagePrototype = PrototypeReader::read(
1441  PrototypeReader::getResourcePathName(Tools::getPathName(foliageBrushPrototype->getFileName()), foliageBrushPrototype->getFileName()),
1442  Tools::getFileName(foliageBrushPrototype->getFileName())
1443  );
1444  } catch (Exception& exception) {
1445  Console::println("TerrainEditorTabController::updateFoliageBrush(): failed to load prototype: " + foliageBrushPrototype->getFileName());
1446  }
1447  }
1448  if (foliagePrototype == nullptr) continue;
1449  foliageBrushPrototypes.push_back(
1450  {
1451  .prototypeId = terrain->getFoliagePrototypeIndex(foliagePrototype),
1452  .count = foliageBrushPrototype->getCount(),
1453  .normalAlign = foliageBrushPrototype->isNormalAlign(),
1454  .rotationXMin = foliageBrushPrototype->getRotationXMin(),
1455  .rotationXMax = foliageBrushPrototype->getRotationXMax(),
1456  .rotationYMin = foliageBrushPrototype->getRotationYMin(),
1457  .rotationYMax = foliageBrushPrototype->getRotationYMax(),
1458  .rotationZMin = foliageBrushPrototype->getRotationZMin(),
1459  .rotationZMax = foliageBrushPrototype->getRotationZMax(),
1460  .scaleMin = foliageBrushPrototype->getScaleMin(),
1461  .scaleMax = foliageBrushPrototype->getScaleMax(),
1462  .heightMin = foliageBrushPrototype->getHeightMin(),
1463  .heightMax = foliageBrushPrototype->getHeightMax(),
1464  .slopeMin = foliageBrushPrototype->getSlopeMin(),
1465  .slopeMax = foliageBrushPrototype->getSlopeMax()
1466  }
1467  );
1468  }
1469 
1470  //
1471  view->setBrush(foliageBrush.brushTexture, brush->getSize(), brush->getDensity());
1472 }
1473 
1474 void TerrainEditorTabController::applyFoliageBrush(BoundingBox& terrainBoundingBox, const Vector3& brushCenterPosition, int64_t deltaTime) {
1475  auto prototype = view->getPrototype();
1476  if (prototype == nullptr) return;
1477 
1478  //
1479  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1480  auto foliageBrushIdx = -1;
1481  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == true) {
1482  foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
1483  } else {
1484  auto foliageBrushPrototypeIdx = -1;
1485  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1486  }
1487  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1488  if (terrain == nullptr) return;
1489  auto brush = terrain->getBrush(foliageBrushIdx);
1490  if (brush == nullptr) return;
1491 
1492  // check if having brush prototypes (ids)
1493  if (currentFoliageBrushOperation == Terrain::BRUSHOPERATION_ADD) {
1494  auto haveBrushPrototypeIds = false;
1495  for (auto& foliageBrushPrototype: foliageBrushPrototypes) {
1496  if (foliageBrushPrototype.prototypeId != -1) haveBrushPrototypeIds = true;
1497  }
1498  if (haveBrushPrototypeIds == false) return;
1499  }
1500 
1501  //
1503  case Terrain::BRUSHOPERATION_ADD:
1504  //
1505  Terrain::applyFoliageDeleteBrush(
1506  terrainBoundingBox,
1507  brushCenterPosition,
1508  foliageBrush,
1510  Terrain::BRUSHOPERATION_DELETE,
1511  prototype->getTerrain()->getFoliageMaps(),
1513  );
1514  //
1515  Terrain::applyFoliageBrush(
1516  terrainBoundingBox,
1517  prototype->getTerrain()->getHeightVector(),
1518  brushCenterPosition,
1519  foliageBrush,
1522  prototype->getTerrain()->getFoliageMaps(),
1524  );
1525  break;
1526  case Terrain::BRUSHOPERATION_DELETE:
1527  //
1528  Terrain::applyFoliageDeleteBrush(
1529  terrainBoundingBox,
1530  brushCenterPosition,
1531  foliageBrush,
1534  prototype->getTerrain()->getFoliageMaps(),
1536  );
1537  break;
1538  }
1539 
1540  //
1543 
1544  //
1545  recreateFoliagePartitions.clear();
1546  Terrain::emptyFoliageMaps(newFoliageMaps);
1547 }
1548 
1550  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_add"))->getController()->getValue().equals("1") == true) {
1551  return Terrain::BRUSHOPERATION_ADD;
1552  } else
1553  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_substract"))->getController()->getValue().equals("1") == true) {
1554  return Terrain::BRUSHOPERATION_SUBTRACT;
1555  } else
1556  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_flatten"))->getController()->getValue().equals("1") == true) {
1557  return Terrain::BRUSHOPERATION_FLATTEN;
1558  } else
1559  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_smooth"))->getController()->getValue().equals("1") == true) {
1560  return Terrain::BRUSHOPERATION_SMOOTH;
1561  } else
1562  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_ramp"))->getController()->getValue().equals("1") == true) {
1563  return Terrain::BRUSHOPERATION_RAMP;
1564  } else
1565  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_terrain_delete"))->getController()->getValue().equals("1") == true) {
1566  return Terrain::BRUSHOPERATION_DELETE;
1567  } else {
1568  return Terrain::BRUSHOPERATION_NONE;
1569  }
1570 }
1571 
1573  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_water_water"))->getController()->getValue().equals("1") == true) {
1574  return Terrain::BRUSHOPERATION_WATER_ADD;
1575  } else
1576  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_water_delete"))->getController()->getValue().equals("1") == true) {
1577  return Terrain::BRUSHOPERATION_WATER_DELETE;
1578  } else {
1579  return Terrain::BRUSHOPERATION_NONE;
1580  }
1581 }
1582 
1584  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_foliage_add"))->getController()->getValue().equals("1") == true) {
1585  return Terrain::BRUSHOPERATION_ADD;
1586  } else
1587  if (required_dynamic_cast<GUIElementNode*>(screenNode->getNodeById(view->getTabId() + "_tab_foliage_delete"))->getController()->getValue().equals("1") == true) {
1588  return Terrain::BRUSHOPERATION_DELETE;
1589  } else {
1590  return Terrain::BRUSHOPERATION_NONE;
1591  }
1592 }
1593 
1596  currentTerrainBrushTexture = nullptr;
1599  TextureReader::read(
1600  Tools::getPathName(currentTerrainBrushTextureFileName),
1601  Tools::getFileName(currentTerrainBrushTextureFileName),
1602  false,
1603  false
1604  );
1605  try {
1606  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTexture(currentTerrainBrushTexture);
1607  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("terrainbrush_texture"))->setTooltip(currentTerrainBrushTextureFileName);
1608  } catch (Exception& exception) {
1609  Console::println("TerrainEditorTabController::setTerrainBrushTexture(): An error occurred: " + string(exception.what()));
1610  showInfoPopUp("Warning", string(exception.what()));
1611  }
1612  view->setBrush(
1616  );
1617 }
1618 
1620  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1621  if (StringTools::startsWith(outlinerNode, "terrain.foliage.") == false) return;
1622  auto foliageBrushIdx = Integer::parse(StringTools::substring(outlinerNode, string("terrain.foliage.").size(), outlinerNode.size()));
1623  auto prototype = view->getPrototype();
1624  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1625  if (terrain == nullptr) return;
1626  auto brush = terrain->getBrush(foliageBrushIdx);
1627  if (brush == nullptr) return;
1628  brush->setFileName(fileName);
1629  try {
1630  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_texture"))->setSource(brush->getFileName());
1631  } catch (Exception& exception) {
1632  Console::println("TerrainEditorTabController::setFoliageBrushTexture(): An error occurred: " + string(exception.what()));
1633  showInfoPopUp("Warning", string(exception.what()));
1634  }
1636 }
1637 
1639  auto prototype = view->getPrototype();
1640  auto terrain = prototype != nullptr?prototype->getTerrain():nullptr;
1641  if (terrain == nullptr) return;
1642 
1643  auto outlinerNode = view->getEditorView()->getScreenController()->getOutlinerSelection();
1644  auto foliageBrushIdx = -1;
1645  auto foliageBrushPrototypeIdx = -1;
1646  if (checkOutlinerFoliageBrushPrototype(outlinerNode, foliageBrushIdx, foliageBrushPrototypeIdx) == false) return;
1647  auto brush = terrain->getBrush(foliageBrushIdx);
1648  if (brush == nullptr) return;
1649  auto brushPrototype = brush->getPrototype(foliageBrushPrototypeIdx);
1650  brushPrototype->setFileName(fileName);
1651  try {
1652  required_dynamic_cast<GUIImageNode*>(screenNode->getNodeById("foliagebrush_prototype_file"))->setSource(brushPrototype->getFileName());
1653  } catch (Exception& exception) {
1654  Console::println("TerrainEditorTabController::setFoliageBrushPrototype(): " + string(exception.what()));
1655  }
1657 }
Engine main class.
Definition: Engine.h:131
Texture entity.
Definition: Texture.h:24
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
const Vector3 & getDimensions() const
Definition: BoundingBox.h:128
void setFileName(const string &fileName)
Set texture file name.
void removeWaterPositionMap(int idx)
Remove water position map at given water position map index.
PrototypeTerrainBrush * getBrush(int idx)
Get prototype terrain brush.
Prototype definition.
Definition: Prototype.h:55
PrototypeTerrain * getTerrain()
Definition: Prototype.h:613
GUI parser.
Definition: GUIParser.h:40
GUI module class.
Definition: GUI.h:64
GUI node controller base class.
GUI node base class.
Definition: GUINode.h:64
const string & getToolTip()
Definition: GUINode.h:346
const string & getId()
Definition: GUINode.h:339
GUI parent node base class thats supporting child nodes.
Definition: GUIParentNode.h:42
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
float getX() const
Definition: Vector3.h:100
float getZ() const
Definition: Vector3.h:134
File system singleton class.
Definition: FileSystem.h:17
void show(int mouseX, int mouseY)
Shows the pop up.
void addMenuItem(const string &text, const string &id, Action *action=nullptr)
Add menu item.
bool isDropOnNode(int dropX, int dropY, const string &nodeId)
Is drop on node.
void setOutlinerSelection(const string &newSelectionValue)
Set outliner selection.
void browseTo(const string &fileName)
Browse to file name.
void show(const string &cwd, const string &captionText, const vector< string > &extensions, const string &fileName, bool enableFilter, Action *applyAction, Action *cancelAction=nullptr, const string &settingsFileName=".filedialog.properties", const string &settingsPathName=string())
Shows the file dialog pop up.
void show(const string &caption, const string &message)
Shows the pop up.
void show(int mouseX, int mouseY, const string &tooltip)
Show tooltip.
Pop ups controller accessor class.
Definition: PopUps.h:29
FileDialogScreenController * getFileDialogScreenController()
Definition: PopUps.h:61
TooltipScreenController * getTooltipScreenController()
Definition: PopUps.h:131
ContextMenuScreenController * getContextMenuScreenController()
Definition: PopUps.h:96
InfoDialogScreenController * getInfoDialogScreenController()
Definition: PopUps.h:75
void onContextMenuRequest(GUIElementNode *node, int mouseX, int mouseY) override
On context menu request.
void onDrop(const string &payload, int mouseX, int mouseY) override
On drop.
void updateInfoText(const MutableString &text)
Update info text line.
void applyFoliageBrushPrototypeDetails()
Apply foliage brush prototype details.
void applyTerrainBrush(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, const Vector3 &brushCenterPosition, int64_t deltaTime)
Apply current brush to terrain at given brush center position.
void setTerrainBrushTexture(const string &fileName)
Set terrain brush texture.
bool determineCurrentBrushHeight(BoundingBox &terrainBoundingBox, vector< Model * > terrainModels, const Vector3 &brushCenterPosition)
Determine current brush flatten height.
vector< unordered_map< int, vector< Transform > > > newFoliageMaps
void onCommand(TabControllerCommand command) override
On command.
void setBrushDensityStrength(float densityStrength)
Set brush density/strength.
bool determineRampHeight(BoundingBox &terrainBoundingBox, vector< Model * > terrainModels, const Vector3 &position, float &height)
Determine ramp height.
bool checkOutlinerFoliageBrushPrototype(const string &outlinerNode, int &foliageBrushIdx, int &foliageBrushPrototypeIdx)
Check if outliner selection is foliage brush prototype.
void setFoliageBrushTexture(const string &fileName)
Set foliage brush texture.
void applyRampTerrainBrush(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, const Vector3 &position, float rotation, const Vector2 &scale, float minHeight, float maxHeight)
Apply current brush to terrain at given brush center position.
void showInfoPopUp(const string &caption, const string &message)
Show the information pop up / modal.
void setFoliageBrushPrototype(const string &fileName)
Set foliage brush prototype.
void onAction(GUIActionListenerType type, GUIElementNode *node) override
void createWater(BoundingBox &terrainBoundingBox, const Vector3 &brushCenterPosition, vector< Model * > &waterModels, Vector3 &waterReflectionEnvironmentMappingPosition)
Create water using a auto fill like algorithm.
void applyFoliageBrush(BoundingBox &terrainBoundingBox, const Vector3 &brushCenterPosition, int64_t deltaTime)
Apply current brush to foliage at given brush center position.
void onTooltipShowRequest(GUINode *node, int mouseX, int mouseY) override
On tooltip show request.
void saveFile(const string &pathName, const string &fileName)
Saving prototype as tmodel prototype.
void recreateFoliage(const unordered_set< int > &partitionIdxSet)
Recreate foliage using render groups at given partition indices that has been transformed to temporar...
void addTemporaryFoliage(const vector< unordered_map< int, vector< Transform >>> &newFoliageMaps)
Add temporary foliage.
void addWater(int waterIdx, vector< Model * > waterModels, const Vector3 &waterReflectionEnvironmentMappingPosition)
Add water.
void updateTemporaryFoliage(const unordered_set< int > &partitionIdxSet)
Update temporary foliage.
void addFoliage()
Add foliage using render groups at given partition indices.
void recreateTemporaryFoliage(const unordered_set< int > &partitionIdxSet)
Recreate temporary foliage at given partition indices.
void setBrush(Texture *texture, float scale, float densityStrength)
Set brush.
void setTerrain(BoundingBox &terrainBoundingBox, vector< Model * > terrainModels)
Set terrain models.
void setOutlinerAddDropDownContent(const string &xml)
Set outliner add drop down content.
Definition: EditorView.cpp:400
void setOutlinerContent(const string &xml)
Set outliner content.
Definition: EditorView.cpp:396
EditorScreenController * getScreenController()
Definition: EditorView.h:69
void setDetailsContent(const string &xml)
Set details content.
Definition: EditorView.cpp:404
bool getCurrentTabTooltipPosition(GUIScreenNode *screenNode, int mouseX, int mouseY, int &tooltipLeft, int &tooltipTop)
Determine current tab tooltip position.
Definition: EditorView.cpp:439
void reloadTabOutliner(const string &newSelectionValue=string())
Reload tab outliner.
Definition: EditorView.cpp:408
Console class.
Definition: Console.h:29
Exception base class.
Definition: ExceptionBase.h:19
Float class.
Definition: Float.h:27
Integer class.
Definition: Integer.h:25
Mutable utf8 aware string class.
Definition: MutableString.h:23
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
Definition: Reference.h:38
String tools class.
Definition: StringTools.h:22
Terrain utility.
Definition: Terrain.h:33
An attribute is a name-value pair.
Definition: tinyxml.h:734
Always the top level node.
Definition: tinyxml.h:1317
The element is a container class.
Definition: tinyxml.h:886
std::exception Exception
Exception base class.
Definition: Exception.h:18
Tab controller, which connects UI with logic.
Definition: TabController.h:34
Action Interface.
Definition: Action.h:11